From 44cf6456327286af22158c0df9eeab1efaf0ee7d Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Sun, 7 Dec 2025 01:38:10 -0500 Subject: [PATCH] [epilogue] Fix v3 scheduler incompatibility with epilogue (#8458) Was caused by checking assignability like`Protobuf` instead of `Protobuf>` This epilogue bug would have also applied to other protobuf-serializable types --- commandsv3/BUILD.bazel | 4 ++++ commandsv3/build.gradle | 2 ++ .../SchedulerIsLoggableWithEpilogue.java | 16 ++++++++++++++++ .../epilogue/processor/ProtobufHandler.java | 18 +++++++++++------- 4 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 commandsv3/src/test/java/org/wpilib/command3/epilogue/SchedulerIsLoggableWithEpilogue.java diff --git a/commandsv3/BUILD.bazel b/commandsv3/BUILD.bazel index c7c41c1750..d3ea907669 100644 --- a/commandsv3/BUILD.bazel +++ b/commandsv3/BUILD.bazel @@ -66,8 +66,12 @@ wpilib_java_library( wpilib_java_junit5_test( name = "commandsv3-java-test", srcs = glob(["src/test/java/**/*.java"]), + plugins = [ + "//epilogue-processor:plugin", + ], deps = [ ":commandsv3-java", + "//epilogue-runtime:epilogue-java", "//hal:hal-java", "//ntcore:ntcore-java", "//wpiannotations", diff --git a/commandsv3/build.gradle b/commandsv3/build.gradle index b097060d1d..fe419f7228 100644 --- a/commandsv3/build.gradle +++ b/commandsv3/build.gradle @@ -27,7 +27,9 @@ dependencies { implementation project(':wpilibj') api("us.hebi.quickbuf:quickbuf-runtime:1.4") testAnnotationProcessor project(':javacPlugin') + testAnnotationProcessor project(':epilogue-processor') testImplementation 'org.mockito:mockito-core:4.1.0' + testImplementation project(':epilogue-runtime') } sourceSets.main.java.srcDir "${projectDir}/src/generated/main/java" diff --git a/commandsv3/src/test/java/org/wpilib/command3/epilogue/SchedulerIsLoggableWithEpilogue.java b/commandsv3/src/test/java/org/wpilib/command3/epilogue/SchedulerIsLoggableWithEpilogue.java new file mode 100644 index 0000000000..e4c07f688f --- /dev/null +++ b/commandsv3/src/test/java/org/wpilib/command3/epilogue/SchedulerIsLoggableWithEpilogue.java @@ -0,0 +1,16 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package org.wpilib.command3.epilogue; + +import org.wpilib.command3.Scheduler; +import org.wpilib.epilogue.Logged; + +// This class will fail to compile if m_scheduler is not considered loggable +// Note that this assumes epilogue is set up correctly. +@Logged +public class SchedulerIsLoggableWithEpilogue { + @Logged(name = "Scheduler (logged via protobuf)") + private final Scheduler m_scheduler = Scheduler.createIndependentScheduler(); +} diff --git a/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/ProtobufHandler.java b/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/ProtobufHandler.java index 148e2281c0..664d14a558 100644 --- a/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/ProtobufHandler.java +++ b/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/ProtobufHandler.java @@ -58,14 +58,20 @@ public class ProtobufHandler extends ElementHandler { return false; } - // eg `Protobuf` instead of the raw `Protobuf` type. The message type doesn't - // really matter here; we can leave it as a wildcard. + // Build a type like `Protobuf>` to match + // implementations such as `SchedulerProto implements Protobuf`. + // Note: Using `ProtoMessage` raw as the upper bound causes assignability to fail; we must use + // `ProtoMessage` as the bound, then wrap that with an extends wildcard. + var protoMessageElement = m_elementUtils.getTypeElement("us.hebi.quickbuf.ProtoMessage"); + var protoMessageWildcard = + m_typeUtils.getDeclaredType(protoMessageElement, m_typeUtils.getWildcardType(null, null)); + var boundedProtoMessageWildcard = m_typeUtils.getWildcardType(protoMessageWildcard, null); + var sharpProtobufType = m_typeUtils.getDeclaredType( m_protobufType, typeElement.asType(), // the serializable type - m_typeUtils.getWildcardType( - m_elementUtils.getTypeElement("us.hebi.quickbuf.ProtoMessage").asType(), null)); + boundedProtoMessageWildcard); boolean hasProto = typeElement.getEnclosedElements().stream() @@ -78,9 +84,7 @@ public class ProtobufHandler extends ElementHandler { field .getModifiers() .containsAll(Set.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)); - var typeMatch = - m_typeUtils.isAssignable( - m_typeUtils.erasure(field.asType()), sharpProtobufType); + var typeMatch = m_typeUtils.isAssignable(field.asType(), sharpProtobufType); return nameMatch && modifiersMatch && typeMatch; }); return m_typeUtils.isAssignable(type, m_serializable) && hasProto;