ExpandEnumAsExecs behaves incorrectly when argument name matches enum value (Editor 5.1)

After a bit of digging and testing, I’ve discovered that the ExpandEnumAsExecs functionality does not behave correctly when the name of the parameter to be expanded is the same (case insensitive) as one of the values in the enum type of the parameter. Specifically, when this is the case, the value of the argument (in the case of input pin expansion) or the executed output pin (in the case of output pin expansion) will always be 0.

I don’t know enough about UHT specifics to call this a “bug” vs an expected behavior, but I did happen to run into this issue in a completely organic way, so I think it bears mentioning and perhaps even a warning in UHT when this case is encountered so users know what’s going on.

Here’s my test class:

//==============================================================================
// EnumBugBP.h
// 
// Jordan Massey
// 24.12.2022
//==============================================================================

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "EnumBugBP.generated.h"

UENUM(BlueprintType)
enum class EPinSlot : uint8 {
    First,
    Second,
    Third,
    Fourth,
    Fifth
};

UENUM(BlueprintType)
enum class EPinSlotSameName : uint8 {
    First,
    PinSlotSameName,
    Third,
    Fourth,
    Fifth
};

UCLASS()
class UEnumBugBP : public UBlueprintFunctionLibrary {
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, Category = "EnumBug", meta = (ExpandEnumAsExecs = "outPath"))
        static void TakeEnumPath(EPinSlot desiredPath, EPinSlot& outPath);
    UFUNCTION(BlueprintCallable, Category = "EnumBug", meta = (ExpandEnumAsExecs = "pinSlot"))
        static void TakeEnumPathSameArgName(EPinSlot desiredPath, EPinSlot& pinSlot);
    UFUNCTION(BlueprintCallable, Category = "EnumBug", meta = (ExpandEnumAsExecs = "third"))
        static void TakeEnumPathArgIsEnumVal(EPinSlot desiredPath, EPinSlot& third);
    UFUNCTION(BlueprintCallable, Category = "EnumBug", meta = (ExpandEnumAsExecs = "outPath"))
        static void TakeEnumPathSameName(EPinSlotSameName desiredPath, EPinSlotSameName& outPath);
    UFUNCTION(BlueprintCallable, Category = "EnumBug", meta = (ExpandEnumAsExecs = "pinSlotSameName"))
        static void TakeEnumPathSameNameSameArgName(EPinSlotSameName desiredPath, EPinSlotSameName& pinSlotSameName);
    UFUNCTION(BlueprintCallable, Category = "EnumBug", meta = (ExpandEnumAsExecs = "fourth"))
        static void TakeEnumPathSameNameSameArgIsEnumVal(EPinSlotSameName desiredPath, EPinSlotSameName& fourth);
};
//==============================================================================
// EnumBugBP.cpp
// 
// Jordan Massey
// 24.12.2022
//==============================================================================

#include "EnumBugBP.h"
#include "Engine.h"

void UEnumBugBP::TakeEnumPath(EPinSlot desiredPath, EPinSlot& outPath) {
    outPath = desiredPath;
}

void UEnumBugBP::TakeEnumPathSameArgName(EPinSlot desiredPath, EPinSlot& pinSlot) {
    pinSlot = desiredPath;
}

void UEnumBugBP::TakeEnumPathArgIsEnumVal(EPinSlot desiredPath, EPinSlot& third) {
    third = desiredPath;
}

void UEnumBugBP::TakeEnumPathSameName(EPinSlotSameName desiredPath, EPinSlotSameName& outPath) {
    outPath = desiredPath;
}

void UEnumBugBP::TakeEnumPathSameNameSameArgName(EPinSlotSameName desiredPath, EPinSlotSameName& pinSlotSameName) {
    pinSlotSameName = desiredPath;
}

void UEnumBugBP::TakeEnumPathSameNameSameArgIsEnumVal(EPinSlotSameName desiredPath, EPinSlotSameName& fourth) {
    fourth = desiredPath;
}

My test comprises two enum cases and three argument cases for 6 total permutations:

  1. Enum without matching values and different arg name
  2. Enum without matching values and arg name same as enum name
  3. Enum without matching values and arg name same as enum value
  4. Enum with matching values and different arg name
  5. Enum with matching values and arg name same as enum name
  6. Enum with matching values and arg name same as enum value

In all cases where the argument name matches an enum value (cases 3, 5, and 6) the executed output pin is always pin 0. This is also true for input pin expansion where the value of the incoming argument is always 0.

I ran into this when using ExpandEnumAsExecs with an enum named EFound and an argument named “found” in a UFUNCTION. Moving forward I’ll avoid this naming conflict, but I’m curious if this is considered a bug or an expected behavior that users should be careful to avoid.

Thanks and Happy Holidays
-Miso