New tests for initializing formal access.

This CL adds tests for previously uncovered elements of the semantics
and includes fixes such that the desired behavior is obtained. In
particular, an `isInitializingFormal` element may now occur in contexts
where it wasn't expected until now, and changes were made to handle it.
It is also checked that a capture of an initializing formal (in a
function literal) captures the parameter, not the field.

R=johnniwinther@google.com

Review URL: https://codereview.chromium.org/2039833002 .
This commit is contained in:
Erik Ernst 2016-06-06 15:55:44 +02:00
parent 2d310f1e67
commit 543a51ff3e
8 changed files with 116 additions and 6 deletions

View file

@ -3859,7 +3859,7 @@ class TryBoxedVariables extends ast.Visitor {
if (Elements.isLocal(element)) {
LocalElement local = element;
if (insideInitializer &&
local.isParameter &&
(local.isParameter || local.isInitializingFormal) &&
local.enclosingElement == currentFunction) {
assert(local.enclosingElement.isConstructor);
// Initializers in an initializer-list can communicate via parameters.
@ -3871,7 +3871,9 @@ class TryBoxedVariables extends ast.Visitor {
// outlive the activation of the function).
markAsCaptured(local);
} else if (inTryStatement) {
assert(local.isParameter || local.isVariable);
assert(local.isParameter ||
local.isVariable ||
local.isInitializingFormal);
// Search for the position of the try block containing the variable
// declaration, or -1 if it is declared outside the outermost try.
int i = tryNestingStack.length - 1;

View file

@ -2573,6 +2573,11 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
} else {
semantics = new StaticAccess.parameter(element);
}
} else if (element.isInitializingFormal &&
compiler.options.enableInitializingFormalAccess) {
error = reportAndCreateErroneousElement(node.selector, name.text,
MessageKind.UNDEFINED_STATIC_SETTER_BUT_GETTER, {'name': name});
semantics = new StaticAccess.finalParameter(element);
} else if (element.isVariable) {
if (element.isFinal || element.isConst) {
error = reportAndCreateErroneousElement(node.selector, name.text,

View file

@ -681,7 +681,12 @@ class TypeCheckerVisitor extends Visitor<DartType> {
assert(invariant(node, element != null,
message: 'Missing element for identifier'));
assert(invariant(
node, element.isVariable || element.isParameter || element.isField,
node,
element.isVariable ||
element.isParameter ||
element.isField ||
(element.isInitializingFormal &&
compiler.options.enableInitializingFormalAccess),
message: 'Unexpected context element ${element}'));
return element.computeType(resolution);
}
@ -762,7 +767,9 @@ class TypeCheckerVisitor extends Visitor<DartType> {
return access;
}
if (receiverElement != null &&
(receiverElement.isVariable || receiverElement.isParameter)) {
(receiverElement.isVariable || receiverElement.isParameter ||
(receiverElement.isInitializingFormal &&
compiler.options.enableInitializingFormalAccess))) {
Link<TypePromotion> typePromotions = typePromotionsMap[receiverElement];
if (typePromotions != null) {
while (!typePromotions.isEmpty) {
@ -1078,7 +1085,9 @@ class TypeCheckerVisitor extends Visitor<DartType> {
}
ElementAccess createPromotedAccess(Element element) {
if (element.isVariable || element.isParameter) {
if (element.isVariable || element.isParameter ||
(element.isInitializingFormal &&
compiler.options.enableInitializingFormalAccess)) {
TypePromotion typePromotion = getKnownTypePromotion(element);
if (typePromotion != null) {
return new PromotedAccess(element, typePromotion.type);
@ -1213,7 +1222,11 @@ class TypeCheckerVisitor extends Visitor<DartType> {
}
}
if (variable != null && (variable.isVariable || variable.isParameter)) {
if (variable != null &&
(variable.isVariable ||
variable.isParameter ||
(variable.isInitializingFormal &&
compiler.options.enableInitializingFormalAccess))) {
DartType knownType = getKnownType(variable);
if (!knownType.isDynamic) {
DartType shownType = elements.getType(node.arguments.head);

View file

@ -682,6 +682,8 @@ final Map<String, Message> MESSAGES = {
template: "The setter '#{memberName}' is not defined for the "
"class '#{className}'.",
usedBy: [dart2js, analyzer],
// TODO(eernst): When this.x access is available, add examples here,
// e.g., "class A { var x; A(this.x) : x = 3; } main() => new A(2);"
examples: const ["class A {} main() { new A().x = 499; }",]),
'NO_SUCH_SUPER_MEMBER': new Message(

View file

@ -0,0 +1,19 @@
// Copyright (c) 2016, 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.
//
// DartOptions=--initializing-formal-access
import "package:expect/expect.dart";
class A {
var x, y;
A(this.x) : y = (() => x);
}
main() {
A a = new A(2);
a.x = 3;
Expect.equals(a.x, 3);
Expect.equals(a.y(), 2);
}

View file

@ -0,0 +1,20 @@
// Copyright (c) 2016, 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.
//
// DartOptions=--initializing-formal-access
import "package:expect/expect.dart";
class A {
var x, y;
// This should cause a warning because `x` is final when
// accessed as an initializing formal.
A(this.x) : y = (() { x = 3; });
}
main() {
A a = new A(2);
Expect.equals(a.x, 2);
Expect.throws(() => a.y(), (e) => e is NoSuchMethodError);
}

View file

@ -0,0 +1,30 @@
// Copyright (c) 2016, 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.
//
// DartOptions=--initializing-formal-access
import "package:expect/expect.dart";
class B {}
class A {
B x, y;
A(this.x) {
// Promote to subtype.
if (x is C) y = x.x;
// Promotion fails, not a subtype.
if (x is A) y = x;
}
}
class C extends A implements B {
C(B x) : super(x);
}
main() {
C c2 = new C(null);
C cc = new C(c2);
Expect.equals(c2.y, null);
Expect.equals(cc.y, c2);
}

View file

@ -0,0 +1,19 @@
// Copyright (c) 2016, 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.
//
// DartOptions=--initializing-formal-access
import "package:expect/expect.dart";
class A {
int x;
String y;
A(this.x) : y = x { y = x; }
}
main() {
A a = new A(null);
Expect.equals(a.x, null);
Expect.equals(a.y, null);
}