External Issue: Overridden method Named Arg defaults not being honored

17649, "glitch", "Overridden method Named Arg defaults not being honored.", "2021-05-06T14:35:28Z"

Summary of Problem

This is an issue involving inheritance, overriding methods and named argument defaults values; a more complete description of the problem is below. In short the default values in the overridden method are not being honored, the parent class's are still being used. I'm not sure if this is expected behavior or a bug; if it's expected maybe the Named Arguments and Default Values documentation could be clarified (Procedures — Chapel Documentation 1.24)

What behavior did you observe when encountering this issue?
I declared a base class Foo with a proc hello and an inheriting class Bar which overrides hello. In proc hello I have named arguments with default values. Foo has one set of defaults and Bar has another. When I declare and create a Foo and test out the defaults I get Foo's defaults; when i declare Bar and construct a Bar and repeat the test I get Bar's defaults; so far so good, everything works as expected. The issue I run into is when I declare a Foo but provide Bar as the concrete implementation. When I invoke the hello proc, it runs the overridden Bar.hello() but it gets Foo's defaults.

What behavior did you expect to observe?
When declaring a Foo and providing Bar as the concrete impl. I expected to get Bar's default values for the named arguments since Bar's hello proc was the one that got invoked.

This may be working as the Chapel team intended but it was unexpected and the documentation on Named Arguments and Default Values (Procedures — Chapel Documentation 1.24) didn't really mention anything about what happens in the case of inheritance.

Is this a blocking issue with no known work-arounds?
This is not a blocking issue, it's really easy to work around, it's more that it was unexpected behavior, see previous note about possibly intended behavior.

Steps to Reproduce

Source Code:
Note, I also made a gist of it here: Chapel code example to illustrate issue with inheritance, overriding methods and named vars. · GitHub

/*
Chapel code example to illustrate issue with inheritance, overriding methods and named vars.
Declare a base class Foo with a hello proc.  It has 3 named args with defaults.
Next Declare Bar which inherits from Foo and overrides Foo's hello proc with its own
default values for the named args.
When we declare a Foo with a concrete implementation of Bar we see that the Bar hello proc
gets invoked as expected, but the default args do not; they are still Foo's defaults.
Since the overridden proc of Bar's gets invoked, I would have expected Bar's defaults to go
along with it, but that's not the case based on the output.
*/
class Foo {

    proc hello(greeting:string="foo default greeting", prefix:string="FOO_default_prefix", suffix:string="FOO_default_suffix"): string throws {
        return "FOO.hello(...)=> greeting:`" + greeting + "`, prefix:`" + prefix + "`,  suffix:`" + suffix + "`";
    }

}

class Bar : Foo {

    override proc hello(greeting:string="bar default greeting", prefix:string = "default_bar[", suffix:string = "]bar_default"): string throws {
        return "I'm in BAR.hello(...)=> greeting:`" + greeting + "`, prefix:`" + prefix + "`,  suffix:`" + suffix + "`";
    }
}


// Create Foo with concrete Foo impl
// 1. Prove we get the defaults
// 2. Pass in greeting and get defaults on prefix & suffix
// 3. Pass in greeting & prefix, get default on suffix
var f:Foo = new Foo();
writeln("----Foo----");
writeln(f.hello());
writeln(f.hello(greeting="hello"));
writeln(f.hello(greeting="hello", prefix="<<<"));

// Create Bar with concrete Bar impl
// 1. Prove we get Bar's defaults
// 2. Pass in greeting and get Bar's defaults on prefix & suffix
// 3. Pass in greeting & prefix, get Bar's default on suffix
var b:Bar = new Bar();
writeln("----Bar----");
writeln(b.hello());
writeln(b.hello(greeting="I'm Bar"));
writeln(b.hello(greeting="I'm Bar", prefix="<<<"));

// Create Foo with conreate Bar impl.
// This uses Bar's overridden proc hello, but doesn't get the Bar default named args, why?
// 1. We see it inokes Bar's hello method in the output, but we get Foo's named defaults
// 2. Pass in greeting, we see Bar's method invoked, but still get Foo defaults on prefix & suffix
// 3. Pass in greetin & suffix, same thing, We see Bar's output message, but still Foo default suffix.
var fb:Foo = new Bar();
writeln("----Foo with Bar as impl----");
writeln(fb.hello());
writeln(fb.hello(greeting="expect Bar defaults"));
writeln(fb.hello(greeting="expect Bar defaults minus passed in prefix", prefix="<<<"));

Compile command:
chpl default-args-example.chpl

Execution command:
./default-args-example -nl 1

Configuration Information

  • Output of chpl --version: chpl version 1.24.1

  • Output of $CHPL_HOME/util/printchplenv --anonymize:
    CHPL_TARGET_PLATFORM: linux64
    CHPL_TARGET_COMPILER: gnu
    CHPL_TARGET_ARCH: x86_64
    CHPL_TARGET_CPU: native
    CHPL_LOCALE_MODEL: flat
    CHPL_COMM: none
    CHPL_TASKS: qthreads
    CHPL_LAUNCHER: none
    CHPL_TIMERS: generic
    CHPL_UNWIND: none
    CHPL_MEM: jemalloc
    CHPL_ATOMICS: cstdlib
    CHPL_GMP: none
    CHPL_HWLOC: bundled
    CHPL_REGEXP: re2
    CHPL_LLVM: none
    CHPL_AUX_FILESYS: none

  • Back-end compiler and version, e.g. gcc --version or clang --version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

Thanks Chapel Team!