[cfe] Thread nullability from source for function types

Closes #38216.

Bug: http://dartbug.com/38216
Change-Id: I33def8e24555099a74fae04d658cbde7af08d7c1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/117727
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Dmitry Stefantsov 2019-09-18 13:58:43 +00:00 committed by commit-bot@chromium.org
parent 0c948521ca
commit a56b4ee790
18 changed files with 228 additions and 41 deletions

View file

@ -4,7 +4,8 @@
library fasta.function_type_builder;
import 'builder.dart' show LibraryBuilder, TypeBuilder, TypeVariableBuilder;
import 'builder.dart'
show LibraryBuilder, NullabilityBuilder, TypeBuilder, TypeVariableBuilder;
import 'package:kernel/ast.dart'
show
@ -32,8 +33,10 @@ class FunctionTypeBuilder extends TypeBuilder {
final TypeBuilder returnType;
final List<TypeVariableBuilder> typeVariables;
final List<FormalParameterBuilder> formals;
final NullabilityBuilder nullabilityBuilder;
FunctionTypeBuilder(this.returnType, this.typeVariables, this.formals);
FunctionTypeBuilder(this.returnType, this.typeVariables, this.formals,
this.nullabilityBuilder);
@override
String get name => null;
@ -68,7 +71,9 @@ class FunctionTypeBuilder extends TypeBuilder {
buffer.write(t?.fullNameForErrors);
}
}
buffer.write(") -> ");
buffer.write(") ->");
nullabilityBuilder.writeNullabilityOn(buffer);
buffer.write(" ");
buffer.write(returnType?.fullNameForErrors);
return buffer;
}
@ -106,7 +111,8 @@ class FunctionTypeBuilder extends TypeBuilder {
namedParameters: namedParameters ?? const <NamedType>[],
typeParameters: typeParameters ?? const <TypeParameter>[],
requiredParameterCount: requiredParameterCount,
typedefType: origin);
typedefType: origin,
nullability: nullabilityBuilder.build(library));
}
Supertype buildSupertype(
@ -143,7 +149,10 @@ class FunctionTypeBuilder extends TypeBuilder {
}
}
FunctionTypeBuilder newType = new FunctionTypeBuilder(
returnType?.clone(newTypes), clonedTypeVariables, clonedFormals);
returnType?.clone(newTypes),
clonedTypeVariables,
clonedFormals,
nullabilityBuilder);
newTypes.add(newType);
return newType;
}

View file

@ -35,6 +35,19 @@ class NullabilityBuilder {
const NullabilityBuilder.omitted()
: _syntacticNullability = SyntacticNullability.omitted;
factory NullabilityBuilder.fromNullability(Nullability nullability) {
switch (nullability) {
case Nullability.nullable:
return const NullabilityBuilder.nullable();
case Nullability.legacy:
return const NullabilityBuilder.legacy();
case Nullability.nonNullable:
case Nullability.neither:
default:
return const NullabilityBuilder.omitted();
}
}
/// Used temporarily in the places that need proper handling of NNBD features.
///
/// Over time the uses of [NullabilityBuilder.pendingImplementation] should be

View file

@ -2835,7 +2835,10 @@ class BodyBuilder extends ScopeListener<JumpTarget>
FormalParameters formals = pop();
UnresolvedType returnType = pop();
List<TypeVariableBuilder> typeVariables = pop();
UnresolvedType type = formals.toFunctionType(returnType, typeVariables);
UnresolvedType type = formals.toFunctionType(
returnType,
libraryBuilder.computeNullabilityFromToken(questionMark != null),
typeVariables);
exitLocalScope();
push(type);
}
@ -3056,7 +3059,10 @@ class BodyBuilder extends ScopeListener<JumpTarget>
if (!libraryBuilder.loader.target.enableNonNullable) {
reportErrorIfNullableType(question);
}
UnresolvedType type = formals.toFunctionType(returnType, typeVariables);
UnresolvedType type = formals.toFunctionType(
returnType,
libraryBuilder.computeNullabilityFromToken(question != null),
typeVariables);
exitLocalScope();
push(type);
functionNestingLevel--;
@ -4046,8 +4052,14 @@ class BodyBuilder extends ScopeListener<JumpTarget>
if (!isFunctionExpression) {
annotations = pop(); // Metadata.
}
FunctionNode function = formals.buildFunctionNode(libraryBuilder,
returnType, typeParameters, asyncModifier, body, token.charOffset);
FunctionNode function = formals.buildFunctionNode(
libraryBuilder,
returnType,
typeParameters,
asyncModifier,
body,
const NullabilityBuilder.pendingImplementation(),
token.charOffset);
if (declaration is FunctionDeclaration) {
VariableDeclaration variable = declaration.variable;
@ -4128,8 +4140,14 @@ class BodyBuilder extends ScopeListener<JumpTarget>
FormalParameters formals = pop();
exitFunction();
List<TypeVariableBuilder> typeParameters = pop();
FunctionNode function = formals.buildFunctionNode(libraryBuilder, null,
typeParameters, asyncModifier, body, token.charOffset)
FunctionNode function = formals.buildFunctionNode(
libraryBuilder,
null,
typeParameters,
asyncModifier,
body,
const NullabilityBuilder.pendingImplementation(),
token.charOffset)
..fileOffset = beginToken.charOffset;
if (constantContext != ConstantContext.none) {
@ -5545,9 +5563,12 @@ class FormalParameters {
List<TypeVariableBuilder> typeParameters,
AsyncMarker asyncModifier,
Statement body,
NullabilityBuilder nullabilityBuilder,
int fileEndOffset) {
FunctionType type =
toFunctionType(returnType, typeParameters).builder.build(library);
toFunctionType(returnType, nullabilityBuilder, typeParameters)
.builder
.build(library);
List<VariableDeclaration> positionalParameters = <VariableDeclaration>[];
List<VariableDeclaration> namedParameters = <VariableDeclaration>[];
if (parameters != null) {
@ -5573,11 +5594,12 @@ class FormalParameters {
..fileEndOffset = fileEndOffset;
}
UnresolvedType toFunctionType(UnresolvedType returnType,
UnresolvedType toFunctionType(
UnresolvedType returnType, NullabilityBuilder nullabilityBuilder,
[List<TypeVariableBuilder> typeParameters]) {
return new UnresolvedType(
new FunctionTypeBuilder(
returnType?.builder, typeParameters, parameters),
new FunctionTypeBuilder(returnType?.builder, typeParameters, parameters,
nullabilityBuilder),
charOffset,
uri);
}

View file

@ -209,7 +209,8 @@ TypeBuilder substituteRange(
}
if (changed) {
return new FunctionTypeBuilder(returnType, variables, formals);
return new FunctionTypeBuilder(
returnType, variables, formals, type.nullabilityBuilder);
}
return type;

View file

@ -114,8 +114,8 @@ class TypeBuilderComputer implements DartTypeVisitor<TypeBuilder> {
new FormalParameterBuilder(null, 0, type, parameter.name, null, -1)
..kind = FormalParameterKind.optionalNamed;
}
return new FunctionTypeBuilder(returnType, typeVariables, formals);
return new FunctionTypeBuilder(returnType, typeVariables, formals,
new NullabilityBuilder.fromNullability(node.nullability));
}
TypeBuilder visitTypeParameterType(TypeParameterType node) {
@ -123,10 +123,8 @@ class TypeBuilderComputer implements DartTypeVisitor<TypeBuilder> {
Class kernelClass = parameter.parent;
Library kernelLibrary = kernelClass.enclosingLibrary;
LibraryBuilder library = loader.builders[kernelLibrary.importUri];
// TODO(dmitryas): Compute the nullabilityBuilder field for the result from
// the nullability field of 'node'.
return new NamedTypeBuilder(
parameter.name, const NullabilityBuilder.pendingImplementation(), null)
return new NamedTypeBuilder(parameter.name,
new NullabilityBuilder.fromNullability(node.nullability), null)
..bind(new TypeVariableBuilder.fromKernel(parameter, library));
}

View file

@ -1388,7 +1388,11 @@ class OutlineBuilder extends StackListener {
TypeBuilder returnType = pop();
List<TypeVariableBuilder> typeVariables = pop();
push(library.addFunctionType(
returnType, typeVariables, formals, functionToken.charOffset));
returnType,
typeVariables,
formals,
library.computeNullabilityFromToken(questionMark != null),
functionToken.charOffset));
}
@override
@ -1401,8 +1405,8 @@ class OutlineBuilder extends StackListener {
if (!library.loader.target.enableNonNullable) {
reportErrorIfNullableType(question);
}
push(library.addFunctionType(
returnType, typeVariables, formals, formalsOffset));
push(library.addFunctionType(returnType, typeVariables, formals,
library.computeNullabilityFromToken(question != null), formalsOffset));
}
@override
@ -1432,8 +1436,9 @@ class OutlineBuilder extends StackListener {
library.beginNestedDeclaration(
TypeParameterScopeKind.functionType, "#function_type",
hasMembers: false);
functionType =
library.addFunctionType(returnType, null, formals, charOffset);
// TODO(dmitryas): Make sure that RHS of typedefs can't have '?'.
functionType = library.addFunctionType(returnType, null, formals,
const NullabilityBuilder.omitted(), charOffset);
} else {
Object type = pop();
typeVariables = pop();

View file

@ -1991,9 +1991,10 @@ class SourceLibraryBuilder extends LibraryBuilder {
TypeBuilder returnType,
List<TypeVariableBuilder> typeVariables,
List<FormalParameterBuilder> formals,
NullabilityBuilder nullabilityBuilder,
int charOffset) {
FunctionTypeBuilder builder =
new FunctionTypeBuilder(returnType, typeVariables, formals);
FunctionTypeBuilder builder = new FunctionTypeBuilder(
returnType, typeVariables, formals, nullabilityBuilder);
checkTypeVariables(typeVariables, null);
// Nested declaration began in `OutlineBuilder.beginFunctionType` or
// `OutlineBuilder.beginFunctionTypedFormalParameter`.

View file

@ -0,0 +1,28 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
typedef F = void Function();
void foo() {}
F bar() => foo;
F? baz() => foo;
void Function() hest() => foo;
void Function()? fisk() => foo;
Function()? foobar(Function()? x) => null;
class A<T> {}
class B extends A<Function()?> {
Function()? method(Function()? x) => null;
}
main() {
void Function() g;
void Function()? f = g;
var fBar = bar();
var fBaz = baz();
var fHest = hest();
var fFisk = fisk();
}

View file

@ -0,0 +1,29 @@
library;
import self as self;
import "dart:core" as core;
typedef F = () → void;
class A<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::A<self::A::T*>*
;
}
class B extends self::A<() →? dynamic> {
synthetic constructor •() → self::B*
;
method method(() →? dynamic x) → () →? dynamic
;
}
static method foo() → void
;
static method bar() → () → void
;
static method baz() → () → void
;
static method hest() → () → void
;
static method fisk() → () →? void
;
static method foobar(() →? dynamic x) → () →? dynamic
;
static method main() → dynamic
;

View file

@ -0,0 +1,40 @@
library;
import self as self;
import "dart:core" as core;
typedef F = () → void;
class A<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::A<self::A::T*>*
: super core::Object::•()
;
}
class B extends self::A<() →? dynamic> {
synthetic constructor •() → self::B*
: super self::A::•()
;
method method(() →? dynamic x) → () →? dynamic
return null;
}
static method foo() → void {}
static method bar() → () → void
return #C1;
static method baz() → () → void
return #C1;
static method hest() → () → void
return #C1;
static method fisk() → () →? void
return #C1;
static method foobar(() →? dynamic x) → () →? dynamic
return null;
static method main() → dynamic {
() → void g;
() →? void f = g;
() → void fBar = self::bar();
() → void fBaz = self::baz();
() → void fHest = self::hest();
() →? void fFisk = self::fisk();
}
constants {
#C1 = tearoff self::foo
}

View file

@ -0,0 +1,40 @@
library;
import self as self;
import "dart:core" as core;
typedef F = () → void;
class A<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::A<self::A::T*>*
: super core::Object::•()
;
}
class B extends self::A<() →? dynamic> {
synthetic constructor •() → self::B*
: super self::A::•()
;
method method(() →? dynamic x) → () →? dynamic
return null;
}
static method foo() → void {}
static method bar() → () → void
return #C1;
static method baz() → () → void
return #C1;
static method hest() → () → void
return #C1;
static method fisk() → () →? void
return #C1;
static method foobar(() →? dynamic x) → () →? dynamic
return null;
static method main() → dynamic {
() → void g;
() →? void f = g;
() → void fBar = self::bar();
() → void fBaz = self::baz();
() → void fHest = self::hest();
() →? void fFisk = self::fisk();
}
constants {
#C1 = tearoff self::foo
}

View file

@ -27,5 +27,5 @@ class Foo extends core::Object {
}
static method main() → dynamic
;
static method test_nullable_function_type_formal_param({() →* core::int f}) → core::int
static method test_nullable_function_type_formal_param({() →? core::int f}) → core::int
;

View file

@ -32,7 +32,7 @@ static method main() → dynamic {
foo.{self::Foo::bar}(6);
self::test_nullable_function_type_formal_param(f: () → core::int* => 2);
}
static method test_nullable_function_type_formal_param({() →* core::int f = #C1}) → core::int {
static method test_nullable_function_type_formal_param({() →? core::int f = #C1}) → core::int {
return let final core::int #t1 = f.call() in #t1.==(null) ?{core::int*} 1.{core::int::unary-}() : #t1;
}

View file

@ -32,7 +32,7 @@ static method main() → dynamic {
foo.{self::Foo::bar}(6);
self::test_nullable_function_type_formal_param(f: () → core::int* => 2);
}
static method test_nullable_function_type_formal_param({() →* core::int f = #C1}) → core::int {
static method test_nullable_function_type_formal_param({() →? core::int f = #C1}) → core::int {
return let final core::int #t1 = f.call() in #t1.==(null) ?{core::int*} 1.{core::int::unary-}() : #t1;
}

View file

@ -2,15 +2,15 @@ library;
import self as self;
import "dart:core" as core;
typedef Typedef1 = ({a: core::int, required b: core::int}) →* dynamic;
typedef Typedef2 = ({a: core::int, required b: core::int}) →* dynamic;
typedef Typedef1 = ({a: core::int, required b: core::int}) → dynamic;
typedef Typedef2 = ({a: core::int, required b: core::int}) → dynamic;
class Class extends core::Object {
synthetic constructor •() → self::Class*
;
method method({core::int a, required core::int b, required final core::int c, required covariant final core::int d}) → dynamic
;
}
static field ({a: core::int, required b: core::int}) →* dynamic field;
static field ({a: core::int, required b: core::int}) → dynamic field;
static method method({core::int a, required core::int b, required final core::int c}) → dynamic
;
static method main() → dynamic

View file

@ -2,15 +2,15 @@ library;
import self as self;
import "dart:core" as core;
typedef Typedef1 = ({a: core::int, required b: core::int}) →* dynamic;
typedef Typedef2 = ({a: core::int, required b: core::int}) →* dynamic;
typedef Typedef1 = ({a: core::int, required b: core::int}) → dynamic;
typedef Typedef2 = ({a: core::int, required b: core::int}) → dynamic;
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
method method({core::int a = #C1, required core::int b = #C1, required final core::int c = #C1, required covariant final core::int d = #C1}) → dynamic {}
}
static field ({a: core::int, required b: core::int}) →* dynamic field;
static field ({a: core::int, required b: core::int}) → dynamic field;
static method method({core::int a = #C1, required core::int b = #C1, required final core::int c = #C1}) → dynamic {}
static method main() → dynamic {}

View file

@ -2,15 +2,15 @@ library;
import self as self;
import "dart:core" as core;
typedef Typedef1 = ({a: core::int, required b: core::int}) →* dynamic;
typedef Typedef2 = ({a: core::int, required b: core::int}) →* dynamic;
typedef Typedef1 = ({a: core::int, required b: core::int}) → dynamic;
typedef Typedef2 = ({a: core::int, required b: core::int}) → dynamic;
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
method method({core::int a = #C1, required core::int b = #C1, required final core::int c = #C1, required covariant final core::int d = #C1}) → dynamic {}
}
static field ({a: core::int, required b: core::int}) →* dynamic field;
static field ({a: core::int, required b: core::int}) → dynamic field;
static method method({core::int a = #C1, required core::int b = #C1, required final core::int c = #C1}) → dynamic {}
static method main() → dynamic {}

View file

@ -831,6 +831,7 @@ instantiate_to_bound/typedef_omitted_bound: TextSerializationFailure # Was: Pass
instantiate_to_bound/typedef_raw_in_bound: TextSerializationFailure # Was: Pass
instantiate_to_bound/typedef_super_bounded_type: TextSerializationFailure # Was: Pass
new_const_insertion/simple: TextSerializationFailure # Was: Pass
nnbd/function_types: TextSerializationFailure
nnbd/late: TextSerializationFailure
nnbd/nullable_param: TextSerializationFailure
nnbd/required: TextSerializationFailure