|
19 | 19 | /// with the same kind of root node.
|
20 | 20 | import 'package:kernel/ast.dart';
|
21 | 21 | import 'package:kernel/clone.dart';
|
| 22 | +import 'package:kernel/src/bounds_checks.dart'; |
22 | 23 | import 'package:kernel/src/printer.dart';
|
23 | 24 | import 'package:kernel/text/ast_to_text.dart' show Precedence, Printer;
|
24 | 25 | import 'package:kernel/type_environment.dart';
|
@@ -3947,8 +3948,14 @@ class OrPattern extends Pattern {
|
3947 | 3948 | };
|
3948 | 3949 | List<VariableDeclaration> declaredVariables = this.declaredVariables;
|
3949 | 3950 | for (VariableDeclaration variable in declaredVariables) {
|
3950 |
| - VariableDeclaration leftVariable = leftVariablesByName[variable.name!]!; |
3951 |
| - VariableDeclaration rightVariable = rightVariablesByName[variable.name!]!; |
| 3951 | + VariableDeclaration? leftVariable = leftVariablesByName[variable.name!]; |
| 3952 | + VariableDeclaration? rightVariable = rightVariablesByName[variable.name!]; |
| 3953 | + |
| 3954 | + if (leftVariable == null || rightVariable == null) { |
| 3955 | + // TODO(cstefantsova): Make sure an error is reported. |
| 3956 | + continue; |
| 3957 | + } |
| 3958 | + |
3952 | 3959 | variable.initializer = inferenceVisitor.engine.forest
|
3953 | 3960 | .createConditionalExpression(
|
3954 | 3961 | fileOffset,
|
@@ -4535,6 +4542,209 @@ class ListPattern extends Pattern {
|
4535 | 4542 | }
|
4536 | 4543 | }
|
4537 | 4544 |
|
| 4545 | +class ObjectPattern extends Pattern { |
| 4546 | + final Reference classReference; |
| 4547 | + final List<NamedPattern> fields; |
| 4548 | + final List<DartType>? typeArguments; |
| 4549 | + Class get classNode => classReference.asClass; |
| 4550 | + |
| 4551 | + ObjectPattern( |
| 4552 | + this.classReference, this.fields, this.typeArguments, int fileOffset) |
| 4553 | + : super(fileOffset) { |
| 4554 | + setParents(fields, this); |
| 4555 | + } |
| 4556 | + |
| 4557 | + @override |
| 4558 | + PatternInferenceResult acceptInference(InferenceVisitorImpl visitor, |
| 4559 | + {required SharedMatchContext context}) { |
| 4560 | + return visitor.visitObjectPattern(this, context: context); |
| 4561 | + } |
| 4562 | + |
| 4563 | + @override |
| 4564 | + List<VariableDeclaration> get declaredVariables { |
| 4565 | + return [for (NamedPattern field in fields) ...field.declaredVariables]; |
| 4566 | + } |
| 4567 | + |
| 4568 | + @override |
| 4569 | + void toTextInternal(AstPrinter printer) { |
| 4570 | + printer.write('${classNode.name}'); |
| 4571 | + if (typeArguments != null) { |
| 4572 | + printer.write('<'); |
| 4573 | + printer.writeTypes(typeArguments!); |
| 4574 | + printer.write('>'); |
| 4575 | + } |
| 4576 | + printer.write('('); |
| 4577 | + String comma = ''; |
| 4578 | + for (Pattern field in fields) { |
| 4579 | + printer.write(comma); |
| 4580 | + field.toTextInternal(printer); |
| 4581 | + comma = ', '; |
| 4582 | + } |
| 4583 | + printer.write(')'); |
| 4584 | + } |
| 4585 | + |
| 4586 | + @override |
| 4587 | + String toString() { |
| 4588 | + return "ObjectPattern(${toStringInternal()})"; |
| 4589 | + } |
| 4590 | + |
| 4591 | + @override |
| 4592 | + PatternTransformationResult transform( |
| 4593 | + Expression matchedExpression, |
| 4594 | + DartType matchedType, |
| 4595 | + Expression variableInitializingContext, |
| 4596 | + InferenceVisitorBase inferenceVisitor) { |
| 4597 | + // targetObjectType: `classNode`<`typeArguments`> |
| 4598 | + DartType targetObjectType; |
| 4599 | + if (typeArguments != null && |
| 4600 | + typeArguments!.length == classNode.typeParameters.length) { |
| 4601 | + targetObjectType = |
| 4602 | + new InterfaceType(classNode, Nullability.nonNullable, typeArguments); |
| 4603 | + } else { |
| 4604 | + if (typeArguments != null) { |
| 4605 | + // TODO(cstefantsova): Report an error. |
| 4606 | + } |
| 4607 | + targetObjectType = new InterfaceType( |
| 4608 | + classNode, |
| 4609 | + Nullability.nonNullable, |
| 4610 | + calculateBounds( |
| 4611 | + classNode.typeParameters, |
| 4612 | + inferenceVisitor.coreTypes.objectClass, |
| 4613 | + inferenceVisitor.libraryBuilder.library)); |
| 4614 | + } |
| 4615 | + |
| 4616 | + bool typeCheckForTargetNeeded = |
| 4617 | + !inferenceVisitor.isAssignable(targetObjectType, matchedType) || |
| 4618 | + matchedType is DynamicType; |
| 4619 | + |
| 4620 | + // objectVariable: `matchedType` OVAR = `matchedExpression` |
| 4621 | + VariableDeclaration objectVariable = inferenceVisitor.engine.forest |
| 4622 | + .createVariableDeclarationForValue(matchedExpression, |
| 4623 | + type: matchedType); |
| 4624 | + |
| 4625 | + // typeCheck: `objectVariable` is `targetObjectType` |
| 4626 | + // ==> OVAR is `classNode`<`typeArguments`> |
| 4627 | + Expression? typeCheck; |
| 4628 | + if (typeCheckForTargetNeeded) { |
| 4629 | + typeCheck = inferenceVisitor.engine.forest.createIsExpression( |
| 4630 | + fileOffset, |
| 4631 | + inferenceVisitor.engine.forest |
| 4632 | + .createVariableGet(fileOffset, objectVariable), |
| 4633 | + targetObjectType, |
| 4634 | + forNonNullableByDefault: false); |
| 4635 | + } |
| 4636 | + |
| 4637 | + List<VariableDeclaration> elementAccessVariables = []; |
| 4638 | + PatternTransformationResult transformationResult = |
| 4639 | + new PatternTransformationResult([]); |
| 4640 | + for (NamedPattern field in fields) { |
| 4641 | + String? fieldNameString; |
| 4642 | + if (field.name.isNotEmpty) { |
| 4643 | + fieldNameString = field.name; |
| 4644 | + } else { |
| 4645 | + // The name is defined by the nested variable pattern. |
| 4646 | + Pattern nestedPattern = field.pattern; |
| 4647 | + if (nestedPattern is VariablePattern) { |
| 4648 | + fieldNameString = nestedPattern.name; |
| 4649 | + } |
| 4650 | + } |
| 4651 | + |
| 4652 | + Expression objectElement; |
| 4653 | + DartType fieldType; |
| 4654 | + if (fieldNameString != null) { |
| 4655 | + Name fieldName = new Name(fieldNameString); |
| 4656 | + |
| 4657 | + ObjectAccessTarget fieldAccessTarget = inferenceVisitor |
| 4658 | + .findInterfaceMember(targetObjectType, fieldName, fileOffset, |
| 4659 | + callSiteAccessKind: CallSiteAccessKind.getterInvocation); |
| 4660 | + |
| 4661 | + if (fieldAccessTarget.member != null) { |
| 4662 | + fieldType = fieldAccessTarget.getGetterType(inferenceVisitor); |
| 4663 | + |
| 4664 | + // objectElement: `objectVariable`.`fieldName` |
| 4665 | + // ==> OVAR.`fieldName` |
| 4666 | + objectElement = new InstanceGet( |
| 4667 | + InstanceAccessKind.Instance, |
| 4668 | + inferenceVisitor.engine.forest |
| 4669 | + .createVariableGet(fileOffset, objectVariable) |
| 4670 | + ..promotedType = targetObjectType, |
| 4671 | + fieldName, |
| 4672 | + resultType: fieldType, |
| 4673 | + interfaceTarget: fieldAccessTarget.member!) |
| 4674 | + ..fileOffset = fileOffset; |
| 4675 | + } else { |
| 4676 | + objectElement = inferenceVisitor.helper.buildProblem( |
| 4677 | + templateUndefinedGetter.withArguments(fieldNameString, |
| 4678 | + targetObjectType, inferenceVisitor.isNonNullableByDefault), |
| 4679 | + fileOffset, |
| 4680 | + noLength); |
| 4681 | + fieldType = const InvalidType(); |
| 4682 | + } |
| 4683 | + } else { |
| 4684 | + objectElement = inferenceVisitor.helper.buildProblem( |
| 4685 | + messageUnspecifiedGetterNameInObjectPattern, fileOffset, noLength); |
| 4686 | + fieldType = const InvalidType(); |
| 4687 | + } |
| 4688 | + |
| 4689 | + // objectElementVariable: `fieldType` EVAR = `objectElement` |
| 4690 | + // ==> `fieldType` EVAR = OVAR.`fieldName` |
| 4691 | + VariableDeclaration objectElementVariable = inferenceVisitor.engine.forest |
| 4692 | + .createVariableDeclarationForValue(objectElement, type: fieldType); |
| 4693 | + |
| 4694 | + PatternTransformationResult subpatternTransformationResult = field.pattern |
| 4695 | + .transform( |
| 4696 | + inferenceVisitor.engine.forest |
| 4697 | + .createVariableGet(fileOffset, objectElementVariable), |
| 4698 | + fieldType, |
| 4699 | + inferenceVisitor.engine.forest |
| 4700 | + .createVariableGet(fileOffset, objectElementVariable), |
| 4701 | + inferenceVisitor); |
| 4702 | + |
| 4703 | + // If the sub-pattern transformation doesn't declare captured variables |
| 4704 | + // and consists of a single empty element, it means that it simply |
| 4705 | + // doesn't have a place where it could refer to the element expression. |
| 4706 | + // In that case we can avoid creating the intermediary variable for the |
| 4707 | + // element expression. |
| 4708 | + // |
| 4709 | + // An example of such sub-pattern is in the following: |
| 4710 | + // |
| 4711 | + // if (x case A(foo: var _) { /* ... */ } |
| 4712 | + if (field.declaredVariables.isNotEmpty || |
| 4713 | + !(subpatternTransformationResult.elements.length == 1 && |
| 4714 | + subpatternTransformationResult.elements.single.isEmpty)) { |
| 4715 | + elementAccessVariables.add(objectElementVariable); |
| 4716 | + transformationResult = transformationResult.combine( |
| 4717 | + subpatternTransformationResult, inferenceVisitor); |
| 4718 | + } |
| 4719 | + } |
| 4720 | + |
| 4721 | + transformationResult = transformationResult.prependElement( |
| 4722 | + new PatternTransformationElement( |
| 4723 | + kind: PatternTransformationElementKind.regular, |
| 4724 | + condition: null, |
| 4725 | + variableInitializers: elementAccessVariables), |
| 4726 | + inferenceVisitor); |
| 4727 | + |
| 4728 | + if (typeCheck != null) { |
| 4729 | + transformationResult = transformationResult.prependElement( |
| 4730 | + new PatternTransformationElement( |
| 4731 | + kind: PatternTransformationElementKind.regular, |
| 4732 | + condition: typeCheck, |
| 4733 | + variableInitializers: []), |
| 4734 | + inferenceVisitor); |
| 4735 | + } |
| 4736 | + |
| 4737 | + transformationResult = transformationResult.prependElement( |
| 4738 | + new PatternTransformationElement( |
| 4739 | + kind: PatternTransformationElementKind.regular, |
| 4740 | + condition: null, |
| 4741 | + variableInitializers: [objectVariable]), |
| 4742 | + inferenceVisitor); |
| 4743 | + |
| 4744 | + return transformationResult; |
| 4745 | + } |
| 4746 | +} |
| 4747 | + |
4538 | 4748 | enum RelationalPatternKind {
|
4539 | 4749 | equals,
|
4540 | 4750 | notEquals,
|
|
0 commit comments