mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 18:19:44 +00:00
[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:
parent
0c948521ca
commit
a56b4ee790
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -209,7 +209,8 @@ TypeBuilder substituteRange(
|
|||
}
|
||||
|
||||
if (changed) {
|
||||
return new FunctionTypeBuilder(returnType, variables, formals);
|
||||
return new FunctionTypeBuilder(
|
||||
returnType, variables, formals, type.nullabilityBuilder);
|
||||
}
|
||||
|
||||
return type;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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`.
|
||||
|
|
28
pkg/front_end/testcases/nnbd/function_types.dart
Normal file
28
pkg/front_end/testcases/nnbd/function_types.dart
Normal 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();
|
||||
}
|
|
@ -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
|
||||
;
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {}
|
||||
|
||||
|
|
|
@ -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 {}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue