diff --git a/docs/language/informal/toplevel-inference.md b/docs/language/informal/toplevel-inference.md new file mode 100644 index 000000000000..e694b7cdf562 --- /dev/null +++ b/docs/language/informal/toplevel-inference.md @@ -0,0 +1,360 @@ +# Toplevel inference + +Owner: leafp@google.com + +Status: Proposal + + +## Top level inference overview + +Top level inference has two phases: + +1. **Method override inference** + * If you omit a return type or parameter type from an overridden or + implemented method, inference will try to fill in the missing type using the + signature of the method you are overriding. +2. **Static variable and field inference** + * If you omit the type of a field, setter, or getter, which overrides a + corresponding member of a superclass, then inference will try to fill in the + missing type using the type of the corresponding member of the superclass. + * Otherwise, declarations of static variables and fields that omit a type + will be inferred from their initializer if present. + +All results from the first phase are available in the second phase. + + +As a general principle, when inference fails it is an error. Tools are free to +behave in implementation specific ways to provide a graceful user experience, +but with respect to the language and its semantics, programs for which inference +fails are unspecified. + + +The intuitive idea behind this proposal is that top level inference works by +first inferring methods, and then doing inference for "field like things" +(static variables, fields, setters, and getters), where we don't allow inference +for "field like things" to depend on the results of inference for other "field +like things" except in cases where the dependencies are statically predictable. + + +Some broad principles for type inference that the language team has agreed to, +and which this proposal is designed to satisfy: +* Type inference should be free to fail with an error, rather than being + required to always produce some answer +* It should not be possible for a programmer to observe a declaration as having + two different types at difference points in the program (e.g. dynamic for + recursive uses, but subsequently int). Some consistent answer (or an error) + should always be produced. See the example below for an approach that + violates this principle. +* The inference for local variables, toplevel variables, and fields, should + either agree or error out. The same expression should not be inferred + differently at different syntactic positions. It’s ok for an expression to be + inferrable at one level but not at another. +* Obvious types should be inferred +* Inferred and annotated types should be treated the same +* Simple intuition for users +* Efficient to implement +* Type arguments should be mostly inferred + + +A motivating example for the current strong mode implementation (that also +serves as a demonstration of its shortcomings) is as follows: + + +```dart +class A { + var x = 3; +} +var a = new A(); +var b = a.x; +``` + + +To ensure stability with respect to cycles and different orderings, current +strong mode does inference in two passes: firstly on the static and top-level +variables, and secondly on fields (method override based inference is mostly +irrelevant for this discussion). This ensures that inference is stable, but it +has several unfortunate consequences, most notably the following. Inference may +result in the same variable being implicitly seen at two different types. In the +example above, if all declarations are in the same library, then b will be +inferred before x has been inferred, and so b will be inferred to have type +dynamic. The same declaration in a separate library will see the type of a.x +after inference, and hence will be inferred to have type `int`. This violates +one of the principles above, and also means that changes to the library +structure that introduce a cycle may change inference results. + + +## Method inference + + +### Method inference + + +Method inference for a method m behaves as if all method inference for all of +its supertypes has already been performed. + + +If a method leaves a type off of its signature (parameter type or return type) +and it does not override or implement anything from a super type, then the +omitted types are treated as dynamic. + + +Otherwise, the missing types are filled in with the type from the overridden or +implemented method. If there are multiple overridden/implemented methods, and +any two of them have non-equal types (declared or inferred) for a parameter +position which is being inferred for the overriding method, it is an error. If +there is no corresponding parameter position in the overridden method to infer +from and the signatures are compatible, it is treated as dynamic +(e.g. overriding a one parameter method with a method that takes a second +optional parameter). Note: if there is no corresponding parameter position in +the overriden method to infer from and the signatures are incompatible +(e.g. overriding a one parameter method with a method that takes a second +non-optional parameter), the inference result is not defined and tools are free +to either emit an error, or to defer the error to override checking. + + +### Setter return types + + +If the return type is left off a setter, the return type is inferred to be +`void`. + + +## Static variable and field inference + + +A “candidate” for inference is one of any of the following kinds of definitions, +for which the type annotation has been omitted: +* A top level variable definition +* A static class level variable definition +* An instance variable, getter or setter definition. + + +The *inference dependencies of a candidate* which is inferred via override +inference are the instance fields, setters, and getters that the candidate +overrides. + + +The *inference dependencies of a candidate* which is inferred via initializer +inference are the *inference dependencies of the initializer expression* used to +infer the type of the candidate. + + +The *inference dependencies of an expression* are as defined below +as part of initializer inference. + + +Informally, the inference dependencies of a candidate are all of the program +elements that contribute to inference for the candidate, and hence which must +have their types inferred before the candidate can have its type inferred. Note +that by design, a top level variable or static class variable never depends on +an instance field, setter, or getter. This means that top level variable and +static class variable inference can be done without reference to the results of +field inference. + + +For each candidate, inference for all of its dependencies must be performed +before inference for the candidate is performed. If this cannot be done +(because of a cycle) it is a static error. Note that it is always possible for +the programmer to break a cycle and hence eliminate the error by adding a type +annotation. + + +### Candidate inference + +#### Top level variables + +The inferred type of a top level variable is the type inferred from its +initializer. + +#### Static class variables + +The inferred type of a static class variable is the type inferred from its +initializer. + +#### Instance field, getter, and setter inference + + +The inferred type of a getter, setter, or field is as follows. Note that we say +that a setter overrides a getter if there is a getter of the same name in some +superclass or interface (explicitly declared or induced by an instance variable +declaration), and similarly for setters overriding getters, fields, etc. + + +A getter, setter or field which overrides/implements only a getter is inferred +to have the type taken from the overridden getter result type. + + +A getter, setter or field which overrides/implements only a setter is inferred +to have the type taken from the overridden setter parameter. + + +A getter or final field which overrides/implements both a setter and a getter is +inferred to have the type taken from the overridden getter result type. + + +A setter which overrides/implements both a setter and a getter is inferred to +have the type taken from the overridden setter parameter type. + + +A non-final field which overrides/implements both a setter and a getter is +inferred to have the type taken from the overridden setter parameter type if +this type is the same as the return type of the overridden getter (if the types +are not the same then inference fails with an error). + + +Note that overriding a field is addressed via the implicit induced getter/setter +pair (or just getter in the case of a final field). + + +A getter or setter with no annotated type that does not override anything is +treated as if it were annotated with dynamic. + + +A field with no annotated type that does not override anything has the type +inferred from its initializer. + + +### Initializer inference + + +Initializer inference works by defining a subset of the expressions in the +language for which we can predictably do inference in a way that is not +sensitive to ordering outside of the static dependencies that are syntactically +apparent in the expression. In particular, field inference cannot change the +outcome of initializer inference. + + +A meta-goal is to keep this inference relatively lightweight and efficient to +implement. + + +In all cases, if initializer inference fails, it is an error and produces no +result. Implementations are free to treat this as a result of dynamic after +emitting the error for the purposes of emitting subsequent error messages, but +the behavior of programs for which initializer inference fails is unspecified. + +#### Expressions with immediately-evident type + +We define a set of expressions with immediately-evident types (called +immediately-evident expressions, abbreviated IE) as follows. Immediately +evident expressions have an inferred type as specified below. Any expression +which is not an IE has no inferred type. For every immediately evident +expression, we also define the set of variable dependencies upon which its +inference relies. + +The intention is that toplevel inference should always return the same result as +local inference, or else produce an error. + + +* null, boolean, numeric, string, type, and symbol literals have their + corresponding type. + * No inference dependencies +* An `await` of an expression has type `T` if the expression is an immediately +evident expression with type `T` or `Future`. + * Inference dependencies are the inference dependencies of the awaited + expression +* A `throw` expression has type `bottom`. + * No inference dependencies +* A parenthesized expression has the inferred type of the sub-expression + * Inference dependencies are the dependencies of the sub-expression +* A conditional expression where both values are immediately evident expressions + has type `T` where `T` is the least upper bound of the inferred type of the + two returned sub-expressions. + * Inference dependencies are the union of the inference dependencies of the + two returned sub-expressions. +* Logical boolean expressions and equality expressions have inferred type + `boolean`. + * No inference dependencies +* Relational expressions, bitwise expressions, and shift expressions are treated +as the appropriate method call as defined in the spec. + * Inference dependencies are those of the left hand operand. +* A multiplicative binary expression is treated as a method call as defined in + the spec, with the same exceptions for when the left hand operand is of type `int`. + * Inference dependencies are those of the left hand operand. +* Applications of the prefix operator `!` have type `boolean` + * No inference dependencies +* Applications of the prefix operators `++` and `--` have the type of the operand. + * Inference dependencies are those of the operand. +* Applications of other prefix operators are treated as method calls. + * Inference dependencies are those of the operand. +* Application of a postfix operator to an expression `e` has the inferred type of `e`. + * Inference dependencies are those of the operand. +* A list literal or a map literal with explicit type arguments has the type + `List` or `Map` respectively, where `` or `` are the + provided type arguments. + * No inference dependencies +* A non-empty list literal with no type arguments, and for which all of the + elements are IEs, has the type `List` where `T` is the least upper bound + of the inferred types of the elements. + * If the elements do not all have an inferred type, it is an error + * The inference dependencies of the list literal are the collected + dependencies of the elements. +* An non-const empty list literal with no type arguments has the type + `List`. + * No inference dependencies. +* A const empty list literal with no type arguments has the type + `List`. + * No inference dependencies. +* A map literal with no type arguments, and for which all of the keys and + values are IEs, has the type `Map` where `K` is the least upper bound of + the inferred types of the provided keys, and `V` is the least upper bound of + the provided values. + * If the keys and values do not all have an inferred type, it is an error. + * The inference dependencies of the map literal are the collected + dependencies of the keys and values. +* An non-const empty map literal with no type arguments has the type + `Map`. + * No inference dependencies. +* A const empty map literal with no type arguments has the type `Map`. + * No inference dependencies. +* A function literal with an expression body for which all parameter types are + type annotated and for which the return expression is an IE and has an + inferred type, has the corresponding function type. + * If the return expression has no inferred type it is an error. + * The inference dependencies are the inference dependencies of the return + expression. + * If the function is marked `async`, then the return type of the function is + inferred as `Future>` where `T` is the inferred type of the + return expression and flatten is as described in the spec. +* An instance creation expression with no omitted type arguments has the + obvious type. + * No inference dependencies +* An `as` expression has the cast to type + * No inference dependencies +* An `is` expression has type boolean + * No inference dependencies +* A simple or qualified identifier referring to a top level function, static + variable, field, getter; or a static class variable, static getter or method; + or an instance method; has the inferred type of the identifier. + * Otherwise, if the identifier has no inferred or annotated type then it is + an error. + * Note: specifically, references to instance fields and instance getters are + disallowed here. + * The inference dependency of the identifier is itself if the identifier is + a candidate for inference. Otherwise there are no inference dependencies. +* A simple identifier denoting a formal parameter which has an annotated type + and is not a promotion candidate is an immediately evident expression and has + the type with which it was annotated. + * Note: the treatment of promotion candidates is still under discussion. + * No inference dependencies. +* A function (or function expression) invocation with no omitted generic + arguments where the applicand is an IE that has an inferred type with a + return type of `T`, has type `T` (with any generic arguments substituted in). + * If the applicand has no inferred type, it is an error. + * The inference dependencies are the inference dependencies of the + applicand. +* A method invocation `o.m(...)` with no omitted type arguments where the + receiver is an IE with inferred type `T` such that `T` provides a signature + for `m` with return type `S` has type `S` (with any generic arguments + substituted in). + * If the receiver has no inferred type, it is an error. + * The inference dependencies are the inference dependencies of the receiver. +* A cascade expression `o..e` where `o` is an IE with type `T`, has inferred + type `T` + * If the cascade target has no inferred type it is an error. + * The inference dependencies are the inference dependencies of the cascade + target. + +Note: Assignment expressions are not immediately evident expressions, and +if-null expressions are not immediately evident expressions. diff --git a/pkg/dev_compiler/tool/global_compile.dart b/pkg/dev_compiler/tool/global_compile.dart index 56f1b0a5d3f3..bdd13e0485fd 100644 --- a/pkg/dev_compiler/tool/global_compile.dart +++ b/pkg/dev_compiler/tool/global_compile.dart @@ -60,7 +60,7 @@ void main(List args) { '--modules=legacy', // TODO(vsm): Change this to use common format. '--single-out-file', '--inline-source-map', - '-p', + '--packages', packageRoot ]; if (metadata) {