Skip to content

Commit

Permalink
Clarify decaying vs. non Meta_Unquotify()
Browse files Browse the repository at this point in the history
Many parts of the system cannot semantically deal with unstable
isotopes.  They must be handled in their unstable form, or
decayed before any common tests are done against them (e.g.
you wouldn't want to make a decision based on testing an
isotopic error for IS_BLOCK() and then ignore the error...)

This injects an assert to the VAL_TYPE() test to catch these
semantically problematic checks.  It also distinguishes the
Meta_Unquotify() operations that want to enforce decay, vs.
the ones that want to produce unstable isotopes, vs. the ones
that are certain they will only produce stable isotopes.
  • Loading branch information
hostilefork committed Oct 9, 2023
1 parent e80e375 commit 9a4728f
Show file tree
Hide file tree
Showing 17 changed files with 112 additions and 141 deletions.
6 changes: 2 additions & 4 deletions src/core/a-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1146,8 +1146,7 @@ REBVAL *RL_rebEntrap(const void *p, va_list *vaptr)
}

if (Is_Meta_Of_Pack(v)) {
Meta_Unquotify(v);
Decay_If_Unstable(v);
Meta_Unquotify_Decayed(v);
Meta_Quotify(v);
}

Expand Down Expand Up @@ -1178,8 +1177,7 @@ REBVAL *RL_rebEntrapInterruptible(
}

if (Is_Meta_Of_Pack(v)) {
Meta_Unquotify(v);
Decay_If_Unstable(v);
Meta_Unquotify_Decayed(v);
Meta_Quotify(v);
}

Expand Down
4 changes: 3 additions & 1 deletion src/core/c-context.c
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,9 @@ REBLEN Find_Symbol_In_Context(
Symbol(const*) symbol,
bool strict
){
if (IS_MODULE(context)) {
Byte heart = HEART_BYTE(context);

if (heart == REB_MODULE) {
//
// Modules hang their variables off the symbol itself, in a linked
// list with other modules who also have variables of that name.
Expand Down
9 changes: 3 additions & 6 deletions src/core/evaluator/c-action.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,15 +205,13 @@ Bounce Action_Executor(Frame(*) f)
if (VAL_PARAM_CLASS(PARAM) == PARAM_CLASS_META) {
if (Is_Meta_Of_Pack(ARG)) {
if (NOT_PARAM_FLAG(PARAM, WANT_PACKS)) {
Meta_Unquotify(ARG);
Decay_If_Unstable(ARG);
Meta_Unquotify_Decayed(ARG);
Meta_Quotify(ARG);
}
}
if (Is_Meta_Of_Raised(ARG)) {
if (NOT_PARAM_FLAG(PARAM, WANT_RAISED)) {
Meta_Unquotify(ARG);
Decay_If_Unstable(ARG);
Meta_Unquotify_Decayed(ARG);
Meta_Quotify(ARG);
}
}
Expand Down Expand Up @@ -848,8 +846,7 @@ Bounce Action_Executor(Frame(*) f)
if (NOT_PARAM_FLAG(PARAM, WANT_PACKS)) {
if (VAL_PARAM_CLASS(PARAM) == PARAM_CLASS_META) {
if (Is_Meta_Of_Pack(ARG)) { // v-- inefficient, but works
Meta_Unquotify(ARG);
Decay_If_Unstable(ARG);
Meta_Unquotify_Decayed(ARG);
Meta_Quotify(ARG);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/core/evaluator/c-eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1528,7 +1528,7 @@ Bounce Evaluator_Executor(Frame(*) f)
goto circled_check;
}

Meta_Unquotify(SPARE);
Meta_Unquotify_Undecayed(SPARE);

if (
var_heart == REB_WORD
Expand All @@ -1537,11 +1537,11 @@ Bounce Evaluator_Executor(Frame(*) f)
goto circled_check;
}

Decay_If_Unstable(SPARE); // if pack in slot, resolve it

if (Is_Raised(SPARE)) // don't hide raised errors if not @
fail (VAL_CONTEXT(SPARE));

Decay_If_Unstable(SPARE); // if pack in slot, resolve it

if (var_heart == REB_BLANK) // [_ ...]:
goto circled_check;

Expand Down
21 changes: 18 additions & 3 deletions src/core/functionals/c-typechecker.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,12 @@ bool Typecheck_Value(
DETAILS_AT(ACT_DETAILS(action), IDX_TYPECHECKER_TYPE)
);
REBU64 bits = Typesets[n];
if (bits & FLAGIT_KIND(VAL_TYPE(v)))
enum Reb_Kind k;
if (Is_Isotope(v) and Is_Isotope_Unstable(v))
k = REB_ISOTOPE;
else
k = VAL_TYPE(v);
if (bits & FLAGIT_KIND(k))
goto test_succeeded;
goto test_failed;
}
Expand All @@ -274,7 +279,12 @@ bool Typecheck_Value(
ACT_DETAILS(action),
IDX_TYPECHECKER_TYPE
);
if (VAL_TYPE(v) == VAL_TYPE_KIND(type_word))
enum Reb_Kind k;
if (Is_Isotope(v) and Is_Isotope_Unstable(v))
k = REB_ISOTOPE;
else
k = VAL_TYPE(v);
if (k == VAL_TYPE_KIND(type_word))
goto test_succeeded;
goto test_failed;
}
Expand Down Expand Up @@ -346,7 +356,12 @@ bool Typecheck_Value(
break; }

case REB_TYPE_WORD: {
if (VAL_TYPE_KIND(test) != VAL_TYPE(v))
enum Reb_Kind k;
if (Is_Isotope(v) and Is_Isotope_Unstable(v))
k = REB_ISOTOPE;
else
k = VAL_TYPE(v);
if (VAL_TYPE_KIND(test) != k)
goto test_failed;
break; }

Expand Down
49 changes: 21 additions & 28 deletions src/core/functionals/n-function.c
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ Bounce Init_Thrown_Unwind_Value(
// level "Frame, action, or index to exit from"
// [frame! action! integer!]
// ^result "Result for enclosing state"
// [<opt> <end> <raised> <pack> any-value!]
// [<opt> <void> <raised> <pack> any-value!]
// ]
//
DECLARE_NATIVE(unwind)
Expand All @@ -520,11 +520,11 @@ DECLARE_NATIVE(unwind)
INCLUDE_PARAMS_OF_UNWIND;

REBVAL *level = ARG(level);
REBVAL *v = ARG(result);

Meta_Unquotify(v);
Copy_Cell(SPARE, ARG(result)); // SPARE can hold unstable isotopes
Meta_Unquotify_Undecayed(SPARE);

return Init_Thrown_Unwind_Value(FRAME, level, v, frame_);
return Init_Thrown_Unwind_Value(FRAME, level, SPARE, frame_);
}


Expand Down Expand Up @@ -553,8 +553,10 @@ DECLARE_NATIVE(definitional_return)
{
INCLUDE_PARAMS_OF_DEFINITIONAL_RETURN;

REBVAL *v = ARG(value);
Frame(*) f = frame_; // frame of this RETURN call (implicit DECLARE_NATIVE arg)
Value(*) atom = Copy_Cell(SPARE, ARG(value)); // SPARE for unstable atoms
Meta_Unquotify_Undecayed(atom);

Frame(*) f = FRAME; // frame of this RETURN call

// Each ACTION! cell for RETURN has a piece of information in it that can
// can be unique (the binding). When invoked, that binding is held in the
Expand Down Expand Up @@ -592,29 +594,20 @@ DECLARE_NATIVE(definitional_return)
const REBPAR *param = ACT_PARAMS_HEAD(target_fun);
assert(KEY_SYM(ACT_KEYS_HEAD(target_fun)) == SYM_RETURN);

if (Is_Meta_Of_Raised(v)) {
Unquasify(v);
Raisify(v); // Meta_Unquotify won't do this, it fail()'s
goto skip_type_check;
}
if (Is_Raised(atom))
goto skip_type_check; // all functions allow returning errors

if (Is_Meta_Of_Nihil(v)) { // RETURN NIHIL
if (GET_PARAM_FLAG(param, VANISHABLE)) {
Init_Nihil(v);
if (Is_Nihil(atom)) { // RETURN NIHIL
if (GET_PARAM_FLAG(param, VANISHABLE))
goto skip_type_check;
}

// !!! Treating a return of NIHIL as a return of NONE helps some
// scenarios, for instance piping UPARSE combinators which do not
// want to propagate pure invisibility. The idea should be reviewed
// to see if VOID makes more sense...but start with a more "ornery"
// value to see how it shapes up.
//
Init_None(v);
}
else {
// Safe to unquotify for type checking
Meta_Unquotify(v);
Init_None(atom);
}

// Check type NOW instead of waiting and letting Eval_Core()
Expand All @@ -627,13 +620,13 @@ 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 (GET_PARAM_FLAG(param, RETURN_NONE) and not Is_None(v))
if (GET_PARAM_FLAG(param, RETURN_NONE) and not Is_None(atom))
fail ("If RETURN: <none> is in a function spec, RETURN NONE only");

if (not TYPE_CHECK(param, v)) {
Decay_If_Unstable(v);
if (not TYPE_CHECK(param, v))
fail (Error_Bad_Return_Type(target_frame, v));
if (not TYPE_CHECK(param, atom)) {
Decay_If_Unstable(atom);
if (not TYPE_CHECK(param, atom))
fail (Error_Bad_Return_Type(target_frame, atom));
}

skip_type_check: { ////////////////////////////////////////////////////////
Expand All @@ -642,10 +635,10 @@ 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 REF(only))
Proxy_Multi_Returns_Core(target_frame, v);
if (not Is_Raised(atom) and not REF(only))
Proxy_Multi_Returns_Core(target_frame, atom);

return Init_Thrown_With_Label(FRAME, v, label);
return Init_Thrown_With_Label(FRAME, atom, label);
}
}

Expand Down
23 changes: 10 additions & 13 deletions src/core/n-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ static Bounce Then_Else_Isotopic_Object_Helper(
if (Is_Meta_Of_Nihil(in))
fail ("THEN/ELSE cannot operate on empty pack! input (e.g. NIHIL)");

Meta_Unquotify(in); // see [1]
Meta_Unquotify_Undecayed(in); // see [1]

if (Is_Raised(in)) { // definitional failure, skip
STATE = ST_THENABLE_REJECTING_INPUT;
Expand Down Expand Up @@ -294,7 +294,7 @@ static Bounce Then_Else_Isotopic_Object_Helper(
FRAME_FLAG_META_RESULT
)){
Copy_Cell(in, SPARE); // cheap reification... (e.g. quoted)
Meta_Unquotify(in); // see [1]
Meta_Unquotify_Stable(in); // see [1]
assert(STATE == ST_THENABLE_INITIAL_ENTRY);
assert(not Is_Isotope(in));
goto test_not_lazy;
Expand Down Expand Up @@ -624,7 +624,7 @@ DECLARE_NATIVE(also) // see `tweak :also 'defer on` in %base-defs.r
return Init_Heavy_Null(OUT); // telegraph null isotope

STATE = ST_ALSO_RUNNING_BRANCH;
return CONTINUE(SPARE, branch, Meta_Unquotify(in));
return CONTINUE(SPARE, branch, Meta_Unquotify_Undecayed(in));

} return_original_input: { //////////////////////////////////////////////////

Expand Down Expand Up @@ -1630,19 +1630,16 @@ DECLARE_NATIVE(throw)
{
INCLUDE_PARAMS_OF_THROW;

REBVAL *v = ARG(value);
Value(*) atom = Copy_Cell(SPARE, ARG(value));

if (Is_Nulled(v))
Init_Void(v); // CONTINUE and CONTINUE VOID act the same
if (Is_Meta_Of_Void(atom))
Init_Heavy_Void(atom);
else if (Is_Meta_Of_Null(atom))
Init_Heavy_Null(atom);
else
Meta_Unquotify(v);
Meta_Unquotify_Undecayed(atom);

if (Is_Void(v))
Init_Heavy_Void(v);
else if (Is_Nulled(v))
Init_Heavy_Null(v);

return Init_Thrown_With_Label(FRAME, v, ARG(name)); // name can be nulled
return Init_Thrown_With_Label(FRAME, atom, ARG(name)); // nulled name ok
}


Expand Down
42 changes: 8 additions & 34 deletions src/core/n-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,7 @@ void Set_Var_May_Fail(
// [<opt> <void> any-value!]
// target "Word or tuple, or calculated sequence steps (from GET)"
// [<void> any-word! any-sequence! any-group! the-block!]
// ^value [<opt> <void> <raised> <pack> any-value!] ; tunnels failure
// ^value [<opt> <void> <raised> any-value!] ; tunnels failure
// /any "Do not error on isotopes"
// /groups "Allow GROUP! Evaluations"
// ]
Expand All @@ -1503,14 +1503,13 @@ DECLARE_NATIVE(set)
REBVAL *target = ARG(target);
REBVAL *v = ARG(value);

Meta_Unquotify(v);
if (Is_Raised(v))
return COPY(v); // !!! Is this tunneling worthwhile?
// !!! Should SET look for isotopic objects specially, with a particular
// interaction distinct from DECAY? Review.

// !!! Isotopic objects may have a particular interaction with SET, that
// is distinct from its reification (which should probably be its DECAY)
if (Is_Meta_Of_Raised(v))
return UNMETA(v); // !!! Is this tunneling worthwhile?

Decay_If_Unstable(v);
Meta_Unquotify_Stable(v);

REBVAL *steps;
if (REF(groups))
Expand Down Expand Up @@ -2514,8 +2513,8 @@ DECLARE_NATIVE(heavy) {
DECLARE_NATIVE(light) {
INCLUDE_PARAMS_OF_LIGHT;

Move_Cell(OUT, Meta_Unquotify(ARG(optional)));
Decay_If_Unstable(OUT);
Move_Cell(OUT, ARG(optional));
Meta_Unquotify_Decayed(OUT);

return OUT;
}
Expand Down Expand Up @@ -2557,31 +2556,6 @@ DECLARE_NATIVE(decay)
}


//
// isotopify-if-falsey: native [
//
// "Turn null and false into their corresponding isotopes"
//
// return: [<opt> <void> any-value!]
// ^optional [<opt> any-value!]
// ]
//
DECLARE_NATIVE(isotopify_if_falsey)
{
INCLUDE_PARAMS_OF_ISOTOPIFY_IF_FALSEY;

REBVAL *v = ARG(optional);

if (Is_Meta_Of_Void(v))
return VOID;

Meta_Unquotify(v);
Isotopify_If_Falsey(v);

return Move_Cell(OUT, v);
}


//
// reify: native [
//
Expand Down
16 changes: 5 additions & 11 deletions src/core/t-logic.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,13 @@ DECLARE_NATIVE(xor_1) // see TO-C-NAME
//
// unless: enfix native [
//
// {Variant of non-short-circuit OR which favors the right-hand side result}
// {Give left hand side when right hand side is not null or void}
//
// return: "Conditionally true or false value (not coerced to LOGIC!)"
// [<opt> any-value!]
// return: [<opt> any-value!]
// left "Expression which will always be evaluated"
// [<opt> any-value!]
// ^right "Expression that's also always evaluated (can't short circuit)"
// [<opt> <void> any-value!] ; not a literal GROUP! as with XOR
// [<opt> <void> <pack> any-value!] ; not literal GROUP! as with XOR
// ]
//
DECLARE_NATIVE(unless)
Expand All @@ -288,15 +287,10 @@ DECLARE_NATIVE(unless)
REBVAL *left = ARG(left);
REBVAL *right = ARG(right);

if (Is_Meta_Of_Void(right)) // if right disappears (no branching), left
if (Is_Meta_Of_Void(right) or Is_Meta_Of_Null(right))
return COPY(left);

Meta_Unquotify(right);

if (Is_Truthy(right))
return COPY(right);

return COPY(left); // preserve the exact truthy or falsey value
return UNMETA(right); // preserve packs
}


Expand Down
Loading

0 comments on commit 9a4728f

Please sign in to comment.