Skip to content

Commit

Permalink
Eliminate RETURN/FORWARD, add RETURN/ONLY
Browse files Browse the repository at this point in the history
There was a concept that if you did not use /FORWARD, RETURN
would decay packs into single values.  This idea was based
on thinking it might be confusing if these two patterns
gave different results:

    return (...)

    ; vs.

    x: (...)
    return :x

As the notion that assignment entails implicit decay has become
more familiar, it seems better to have a DECAY native that you
use to avoid returning a pack as-is.  This keeps /FORWARD
refinements from popping up where they shouldn't be (such as
in THROW/FORWARD, so that THROW can be used with packs).

Among the consequences of this change are that the concept
that multi-return functions that wish to step out of the
multi-return paradigm and return single values like pure
NULL or VOID sometimes need another way to do it.  This is
needed since all multi-return PACKs appear to be values to
things like THEN and ELSE.  RETURN/ONLY is introduced as
a fledgling mechanic for this...but if it returns a PACK
it should likely type check it.

The commit also adds type checking for multi-returns, which
were not checked before.
  • Loading branch information
hostilefork committed Jul 27, 2023
1 parent 20a50ce commit 0a2acad
Show file tree
Hide file tree
Showing 19 changed files with 126 additions and 148 deletions.
11 changes: 9 additions & 2 deletions extensions/stdio/mod-stdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,13 @@ DECLARE_NATIVE(read_stdin)
if (Term_IO) {
if (rebRunThrows(
OUT, // <-- output cell
"as binary! try read-line"
"as binary! maybe read-line"
)){
return THROWN;
}
if (Is_Nulled(OUT))
return nullptr; // don't proxy multi-returns

Init_Logic(ARG(eof), false); // never terminates?
return Proxy_Multi_Returns(frame_);
}
Expand Down Expand Up @@ -369,9 +372,13 @@ DECLARE_NATIVE(read_line)
}
#endif

Init_Logic(ARG(eof), eof);
Copy_Cell(OUT, line);
rebRelease(line);

if (Is_Nulled(OUT))
return nullptr;

Init_Logic(ARG(eof), eof);
return Proxy_Multi_Returns(frame_);
}

Expand Down
2 changes: 1 addition & 1 deletion scripts/prot-tls.r
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ parse-asn: func [
; move to the competed state.

make-state-updater: func [
return: [action!]
return: [activation!]
direction "READ or WRITE"
[word!]
transdialect "dialected mapping from state to legal next states"
Expand Down
16 changes: 11 additions & 5 deletions src/core/evaluator/c-action.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,16 @@ bool Lookahead_To_Sync_Enfix_Defer_Flag(Feed(*) feed) {
// UNWIND. We already force type checking through the returns, so this (along
// with any typechecking) should also be done.
//
// 1. At one time this code would not do proxying if the value to return was
// null or void. This was to have parity with a usermode RETURN which
// implemented the same policy. However that policy proved to be a burden
// on usermode RETURN, so it was changed to require explicit suppression
// of multi-return proxying via RETURN/ONLY. Hence natives now need to
// take that responsibility of choosing whether or not to proxy.
//
Bounce Proxy_Multi_Returns_Core(Frame(*) f, Value(*) v)
{
if (Is_Void(v))
return v;

if (Is_Nulled(v))
return v;
assert(not Is_Raised(v));

StackIndex base = TOP_INDEX;

Expand All @@ -153,6 +156,9 @@ Bounce Proxy_Multi_Returns_Core(Frame(*) f, Value(*) v)
if (VAL_PARAM_CLASS(PARAM) != PARAM_CLASS_OUTPUT)
continue;

if (not TYPE_CHECK(PARAM, ARG))
fail (Error_Arg_Type(f, KEY, PARAM, ARG));

Meta_Quotify(Copy_Cell(PUSH(), ARG));
}

Expand Down
19 changes: 6 additions & 13 deletions src/core/functionals/n-function.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ DECLARE_NATIVE(unwind)
//
// return: [] ; !!! notation for "divergent?"
// ^value [<opt> <void> <fail> <pack> any-value!]
// /forward "Pass on parameter packs, vs. just the first pack item"
// /only "Do not do proxying of output variables, just return argument"
// ]
//
DECLARE_NATIVE(definitional_return)
Expand Down Expand Up @@ -627,18 +627,11 @@ DECLARE_NATIVE(definitional_return)
// take [<opt> any-value!] as its argument, and then report the error
// itself...implicating the frame (in a way parallel to this native).
//
if (Is_Isotope(v)) {
//
// allow, so that you can say `return ~xxx~` in functions whose spec
// is written as `return: []`

if (Is_Pack(v) and not Is_Nihil(v) and not REF(forward))
Decay_If_Unstable(v);
if (GET_PARAM_FLAG(param, RETURN_NONE) and not Is_None(v))
fail ("If RETURN: <none> is in a function spec, RETURN NONE only");

if (GET_PARAM_FLAG(param, RETURN_NONE) and not Is_None(v))
fail ("If RETURN: <none> is in a function spec, RETURN NONE only");
}
else {
if (not TYPE_CHECK(param, v)) {
Decay_If_Unstable(v);
if (not TYPE_CHECK(param, v))
fail (Error_Bad_Return_Type(target_frame, v));
}
Expand All @@ -649,7 +642,7 @@ DECLARE_NATIVE(definitional_return)
Copy_Cell(label, Lib(UNWIND)); // see Make_Thrown_Unwind_Value
TG_Unwind_Frame = target_frame;

if (not Is_Raised(v) and not Is_Nulled(v) and not Is_Void(v))
if (not Is_Raised(v) and not REF(only))
Proxy_Multi_Returns_Core(target_frame, v);

return Init_Thrown_With_Label(FRAME, v, label);
Expand Down
3 changes: 3 additions & 0 deletions src/core/l-scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -3222,6 +3222,9 @@ DECLARE_NATIVE(transcode)
VAL_INDEX_RAW(rest) += BIN_TAIL(VAL_STRING(source)) - bp;
}

if (Is_Nulled(OUT))
return nullptr; // don't proxy multi-returns

return Proxy_Multi_Returns(frame_);
}}

Expand Down
2 changes: 1 addition & 1 deletion src/core/n-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,7 @@ DECLARE_NATIVE(get)
if (Is_Isotope(OUT) and not Is_Isotope_Get_Friendly(OUT))
fail (Error_Bad_Word_Get(source, OUT));

return Proxy_Multi_Returns(frame_);
return OUT;
}


Expand Down
7 changes: 2 additions & 5 deletions src/core/t-binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,11 +472,8 @@ REBTYPE(Binary)
&size, v, tail, pattern, flags, skip
);

if (ret >= cast(REBLEN, tail)) {
Init_Nulled(OUT);
Init_Nulled(ARG(tail));
return Proxy_Multi_Returns(frame_);
}
if (ret >= cast(REBLEN, tail))
return nullptr; // Don't Proxy_Multi_Returns()

if (id == SYM_FIND) {
Init_Series_Cell_At(
Expand Down
21 changes: 6 additions & 15 deletions src/core/t-block.c
Original file line number Diff line number Diff line change
Expand Up @@ -963,11 +963,8 @@ REBTYPE(Array)
skip
);

if (find == NOT_FOUND) {
Init_Nulled(OUT);
Init_Nulled(ARG(tail));
return Proxy_Multi_Returns(frame_);
}
if (find == NOT_FOUND)
return nullptr; // don't Proxy_Multi_Returns

REBLEN ret = cast(REBLEN, find);
assert(ret <= limit);
Expand Down Expand Up @@ -1452,7 +1449,7 @@ DECLARE_NATIVE(engroup)
//
// return: [<opt> block!]
// accumulator [<opt> block!]
// ^result [<opt> any-value!]
// result [<void> element? splice?]
// ]
//
DECLARE_NATIVE(glom)
Expand All @@ -1478,20 +1475,14 @@ DECLARE_NATIVE(glom)
//
bool splice = false;

if (Is_Meta_Of_Null(result))
if (Is_Void(result))
return COPY(accumulator);

if (IS_QUASI(result)) {
if (not Is_Meta_Of_Splice(result))
fail ("Only isotopes accepted by GLOM are BLOCK! for splicing");

if (Is_Splice(result)) {
splice = true;
Unquasify(result);
assert(HEART_BYTE(result) == REB_GROUP);
mutable_HEART_BYTE(result) = REB_BLOCK; // interface is for blocks
}
else {
Unquotify(result, 1);
mutable_QUOTE_BYTE(result) = UNQUOTED_1;
}

if (Is_Nulled(accumulator)) {
Expand Down
7 changes: 2 additions & 5 deletions src/core/t-string.c
Original file line number Diff line number Diff line change
Expand Up @@ -945,11 +945,8 @@ REBTYPE(String)
&len, v, tail, ARG(pattern), flags, skip
);

if (find == NOT_FOUND) {
Init_Nulled(OUT);
Init_Nulled(ARG(tail));
return Proxy_Multi_Returns(frame_);
}
if (find == NOT_FOUND)
return nullptr; // don't Proxy_Multi_Returns

REBLEN ret = cast(REBLEN, find);
assert(ret <= cast(REBLEN, tail));
Expand Down
12 changes: 6 additions & 6 deletions src/mezz/base-funcs.r
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ return: func* [] [
func: func* [
{Make action with set-words as locals, <static>, <in>, <with>, <local>}

return: [action!]
return: [activation!]
spec "Help string (opt) followed by arg words (and opt type and string)"
[block!]
body "The body block of the function"
Expand Down Expand Up @@ -729,11 +729,11 @@ attempt: func [

return: "Returns NULL on failure (-or- if last evaluative result is NULL)"
[<opt> <void> any-value!]
@error [error!]
code [block!]
<local> temp
][
if error? error: entrap code [return null]
return (unmeta error, elide error: ~)
if error? temp: entrap code [return null]
return unmeta temp
]

trap: func [
Expand All @@ -759,7 +759,7 @@ trap+: func [
; the result.
;
if error? result: entrap code [
return/forward pack [result null]
return pack [result null]
]

return isotopic make object! [
Expand Down Expand Up @@ -902,7 +902,7 @@ eval-all: func [
meth: enfix func [
{FUNC variant that creates an ACTION! implicitly bound in a context}

return: [action!]
return: [activation!]
:member [set-word! set-path!]
spec [block!]
body [block!]
Expand Down
2 changes: 1 addition & 1 deletion src/mezz/mezz-dump.r
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ dump-to-newline: adapt :dump [
dumps: enfix function [
{Fast generator for dumping function that uses assigned name for prefix}

return: [action!]
return: [activation!]
:name [set-word!]
:value "If issue, create non-specialized dumper...#on or #off by default"
[issue! text! integer! word! set-word! set-path! group! block!]
Expand Down
4 changes: 2 additions & 2 deletions src/mezz/mezz-series.r
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ replace: function [
{Replaces a search value with the replace value within the target series}

return: [any-series!]
@tail "Tail position after last replacement"
[any-series!]
@tail "Tail position after replacement (not applicable if /ALL)"
[<opt> any-series!]
target "Series to replace within (modified)"
[any-series!]
^pattern' "Value to be replaced (converted if necessary)"
Expand Down
2 changes: 1 addition & 1 deletion src/mezz/sys-base.r
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ rescue+: func [ ; see also TRAP+
; the result.
;
if error? result: enrescue code [
return/forward pack [result null]
return pack [result null]
]

return isotopic make object! [
Expand Down
12 changes: 7 additions & 5 deletions src/mezz/sys-load.r
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ transcode-header: func [
{Try to match a data binary! as being a script, definitional fail if not}

return: [<opt> block!]
@rest [binary!]
@rest [<opt> binary!]
@line [integer!]

data [binary!]
Expand All @@ -53,13 +53,13 @@ transcode-header: func [
return raise e
]
if not rest [
return/forward heavy null
return null
]
[hdr /rest]: transcode/one/file/line rest file line except e -> [ ; BLOCK!
return raise e
]

return/forward heavy all [key = 'REBOL, block? hdr] then [hdr]
return all [key = 'REBOL, block? hdr] then [hdr]
]


Expand Down Expand Up @@ -131,7 +131,7 @@ load-header: function [
return either required ['no-header] [
body: data
final: tail of data
return/forward heavy null ; no header object, keep multireturn
return null ; no header object, keep multireturn
]
]

Expand Down Expand Up @@ -221,7 +221,7 @@ load: func [
return: "BLOCK! if Rebol code, otherwise value(s) appropriate for codec"
[<opt> any-value!]
@header "Request the Rebol header object be returned as well"
[object!]
[<opt> object!]
source "Source of the information being loaded"
[<maybe> file! url! tag! the-word! text! binary!]
/type "E.g. rebol, text, markup, jpeg... (by default, auto-detected)"
Expand All @@ -248,6 +248,7 @@ load: func [
; to return that block...do that for now, for compatibility with
; the tests until more work is done.
;
header: null
return data
]
]
Expand All @@ -263,6 +264,7 @@ load: func [

if not find [unbound rebol] type [
if find system.options.file-types type [
header: null
return decode type :data
]

Expand Down
Loading

0 comments on commit 0a2acad

Please sign in to comment.