diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs index baf782af05c..443019ee87b 100644 --- a/pkg/analyzer/lib/src/summary/format.fbs +++ b/pkg/analyzer/lib/src/summary/format.fbs @@ -758,7 +758,14 @@ enum UnlinkedExprOperation : byte { * the nth enclosing function element. Then, push the mth local function of * that element onto the stack. */ - pushLocalFunctionReference + pushLocalFunctionReference, + + /** + * Pop the top two values from the stack. If the first value is non-null, + * keep it and discard the second. Otherwise, keep the second and discard the + * first. + */ + ifNull } /** diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart index d960c4bbfd9..aeea3beb537 100644 --- a/pkg/analyzer/lib/src/summary/idl.dart +++ b/pkg/analyzer/lib/src/summary/idl.dart @@ -2333,6 +2333,13 @@ enum UnlinkedExprOperation { * that element onto the stack. */ pushLocalFunctionReference, + + /** + * Pop the top two values from the stack. If the first value is non-null, + * keep it and discard the second. Otherwise, keep the second and discard the + * first. + */ + ifNull, } /** diff --git a/pkg/analyzer/lib/src/summary/link.dart b/pkg/analyzer/lib/src/summary/link.dart index 6b2e1d711bb..f4486bb0e96 100644 --- a/pkg/analyzer/lib/src/summary/link.dart +++ b/pkg/analyzer/lib/src/summary/link.dart @@ -2265,6 +2265,9 @@ class ExprTypeComputer { case UnlinkedExprOperation.pushParameter: stack.add(_findParameterType(_getNextString())); break; + case UnlinkedExprOperation.ifNull: + _doIfNull(); + break; default: // TODO(paulberry): implement. throw new UnimplementedError('$operation'); @@ -2404,6 +2407,14 @@ class ExprTypeComputer { }()); } + void _doIfNull() { + DartType secondType = stack.removeLast(); + DartType firstType = stack.removeLast(); + DartType type = _leastUpperBound(firstType, secondType); + type = _dynamicIfNull(type); + stack.add(type); + } + void _doInvokeConstructor() { int numNamed = _getNextInt(); int numPositional = _getNextInt(); diff --git a/pkg/analyzer/lib/src/summary/resynthesize.dart b/pkg/analyzer/lib/src/summary/resynthesize.dart index 30f0aec60f5..568736a5831 100644 --- a/pkg/analyzer/lib/src/summary/resynthesize.dart +++ b/pkg/analyzer/lib/src/summary/resynthesize.dart @@ -496,6 +496,9 @@ class _ConstExprBuilder { 'Unable to resolve constructor parameter: $name')); _push(identifier); break; + case UnlinkedExprOperation.ifNull: + _pushBinary(TokenType.QUESTION_QUESTION); + break; case UnlinkedExprOperation.assignToRef: case UnlinkedExprOperation.assignToProperty: case UnlinkedExprOperation.assignToIndex: diff --git a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart index 4662be8c319..f7e69113528 100644 --- a/pkg/analyzer/lib/src/summary/summarize_const_expr.dart +++ b/pkg/analyzer/lib/src/summary/summarize_const_expr.dart @@ -486,6 +486,8 @@ abstract class AbstractConstExprSerializer { operations.add(UnlinkedExprOperation.lessEqual); } else if (operator == TokenType.PERCENT) { operations.add(UnlinkedExprOperation.modulo); + } else if (operator == TokenType.QUESTION_QUESTION) { + operations.add(UnlinkedExprOperation.ifNull); } else { throw new StateError('Unknown operator: $operator'); } diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart index a936003166a..41f5503828f 100644 --- a/pkg/analyzer/test/src/summary/resynthesize_common.dart +++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart @@ -2302,6 +2302,12 @@ const vIdentical = (1 == 2) ? 11 : 22; '''); } + test_const_topLevel_ifNull() { + checkLibrary(r''' +const vIfNull = 1 ?? 2.0; +'''); + } + test_const_topLevel_literal() { checkLibrary(r''' const vNull = null; diff --git a/pkg/analyzer/test/src/summary/summary_common.dart b/pkg/analyzer/test/src/summary/summary_common.dart index 50e7789453a..16f3f3c3b12 100644 --- a/pkg/analyzer/test/src/summary/summary_common.dart +++ b/pkg/analyzer/test/src/summary/summary_common.dart @@ -1755,6 +1755,18 @@ var v = (() { ]); } + test_constExpr_binary_qq() { + UnlinkedVariable variable = serializeVariableText('const v = 1 ?? 2;'); + _assertUnlinkedConst(variable.initializer.bodyExpr, operators: [ + UnlinkedExprOperation.pushInt, + UnlinkedExprOperation.pushInt, + UnlinkedExprOperation.ifNull + ], ints: [ + 1, + 2 + ]); + } + test_constExpr_binary_subtract() { UnlinkedVariable variable = serializeVariableText('const v = 1 - 2;'); _assertUnlinkedConst(variable.initializer.bodyExpr, operators: [