From 61d0b5513dc829fbbaf40dedbea5b753b6d48893 Mon Sep 17 00:00:00 2001 From: Roberto Lublinerman Date: Mon, 12 Jan 2026 18:55:20 -0800 Subject: [PATCH] Implement unconditional patterns. Nested binding patterns where the types are assignable don't require instanceof checks and allow nulls. PiperOrigin-RevId: 855474541 --- .../translate/InstanceOfPatternRewriter.java | 33 ++++++++++++++----- .../InstanceOfPatternRewriterTest.java | 6 ++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/translator/src/main/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriter.java b/translator/src/main/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriter.java index 8f759bc7a3..4ff048a149 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriter.java +++ b/translator/src/main/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriter.java @@ -99,7 +99,8 @@ public void endVisit(InstanceofExpression node) { List variablesToDeclare = new ArrayList<>(); Expression condition = - computePatternCondition(expression, node.getPattern(), variablesToDeclare); + computePatternCondition( + expression, node.getPattern(), /* allowUnconditional= */ false, variablesToDeclare); for (var variableToDeclare : variablesToDeclare) { // Initialize the patternVariables to null. The implementation of patterns in switches creates @@ -124,7 +125,10 @@ public void endVisit(InstanceofExpression node) { } private Expression computePatternCondition( - Expression expression, Pattern pattern, List variablesToDeclare) { + Expression expression, + Pattern pattern, + boolean allowUnconditional, + List variablesToDeclare) { switch (pattern) { case BindingPattern bindingPattern -> { VariableElement patternVariable = bindingPattern.getVariable().getVariableElement(); @@ -137,9 +141,12 @@ private Expression computePatternCondition( .setTypeMirror(typeUtil.getBoolean()); } + boolean isUnconditional = allowUnconditional + && typeUtil.isAssignable( + expression.getTypeMirror(), patternVariable.asType()); + Expression instanceofLhs = expression.copy(); - if (!(expression instanceof SimpleName) - && !patternVariable.asType().getKind().isPrimitive()) { + if (!(expression instanceof SimpleName) && !isUnconditional) { // Use a temporary variable to avoid evaluating the expression more than once and make // sure that user written property getters that might have side-effects are only // evaluated once. @@ -154,9 +161,9 @@ private Expression computePatternCondition( variablesToDeclare.add(patternVariable); // expression instanceof T && (patternVariable = (T) expression, true) return andCondition( - patternVariable.asType().getKind().isPrimitive() - // Primitives don't needs any checking. In the future when/if primitive patterns are - // incorporated in the Java language, this should be updated. + isUnconditional + // Unconditional patterns don't needs any checking. In the future when/if primitive + // patterns are incorporated in the Java language, this should be updated. ? null : new InstanceofExpression() .setLeftOperand(instanceofLhs) @@ -178,7 +185,13 @@ private Expression computePatternCondition( // (e instanceof Rec rec) Expression condition = computePatternCondition( - expression, new BindingPattern(tempVariable), variablesToDeclare); + expression, + new BindingPattern(tempVariable), + // The binding pattern resulting from the implementation of a deconstruction pattern + // cannot be uncondiional since its components will be accessed and hence it cannot + // allow nulls. + /* allowUnconditional */ false, + variablesToDeclare); var recordType = (TypeElement) ((ClassType) deconstructionPattern.getTypeMirror()).tsym; for (int i = 0; i < deconstructionPattern.getNestedPatterns().size(); i++) { @@ -198,7 +211,9 @@ private Expression computePatternCondition( condition = andCondition( - condition, computePatternCondition(property, nestedPattern, variablesToDeclare)); + condition, + computePatternCondition( + property, nestedPattern, /* allowUnconditional= */ true, variablesToDeclare)); } return condition; } diff --git a/translator/src/test/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriterTest.java b/translator/src/test/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriterTest.java index 7b08fc7707..6b97ea43c1 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriterTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriterTest.java @@ -191,18 +191,16 @@ void test() { id comp = nil; Test_A *rec_1 = nil; int32_t i1 = 0; - NSString *comp_1 = nil; NSString *s = nil; int32_t i2 = 0; - Test_A *comp_2 = nil; Test_A *a = nil; id o = create_Test_B_initWithId_withInt_withTest_A_(create_Test_A_initWithInt_withNSString_(1, @""), 3, create_Test_A_initWithInt_withNSString_(2, @"bye")); if ([o isKindOfClass:[Test_B class]] && (rec = (Test_B *) o, true)\ && [comp = [((Test_B *) nil_chk(rec)) a1] isKindOfClass:[Test_A class]] && (rec_1 = (Test_A *) comp, true)\ && (i1 = (int32_t) [((Test_A *) nil_chk(rec_1)) i], true)\ - && [comp_1 = [((Test_A *) nil_chk(rec_1)) s] isKindOfClass:[NSString class]] && (s = comp_1, true)\ + && (s = [((Test_A *) nil_chk(rec_1)) s], true)\ && (i2 = (int32_t) [((Test_B *) nil_chk(rec)) i], true)\ - && [comp_2 = [((Test_B *) nil_chk(rec)) a2] isKindOfClass:[Test_A class]] && (a = comp_2, true)) { + && (a = [((Test_B *) nil_chk(rec)) a2], true)) { """); } }