Skip to content

Commit

Permalink
Change semantics of final members in user-classes
Browse files Browse the repository at this point in the history
Instead of just meaning that they aren't inherited by sub-classes, they can now also only be assigned when they weren't assigned already (more closely mirroring the meaning of the java `final` modifier). To get the old behavior though, you can add `varying` to the declaration.
  • Loading branch information
GrieferAtWork committed Mar 15, 2024
1 parent 7373390 commit e4753f4
Show file tree
Hide file tree
Showing 24 changed files with 440 additions and 151 deletions.
16 changes: 8 additions & 8 deletions include/deemon/asm.h
Original file line number Diff line number Diff line change
Expand Up @@ -956,10 +956,10 @@
#define ASM_CALLATTR_THIS_C_TUPLE 0xd5 /* [2][-1,+1] `push callattr this, const <imm8>, pop...' - Pop a Tuple and lookup and call an attribute of `this', using a string in constant slot `<imm8>' (Only valid for code with the `CODE_FTHISCALL' flag set) */
#define ASM_CALLATTR_C_SEQ 0xd6 /* [3][-1-n,+1] `callattr top, const <imm8>, [#<imm8>]' - Call an attribute <imm8> with a single sequence-like argument packed from the top #<imm8> stack-items. */
#define ASM_CALLATTR_C_MAP 0xd7 /* [3][-1-n,+1] `callattr top, const <imm8>, {#<imm8>*2}' - Call an attribute <imm8> with a single mapping-like argument packed from the top #<imm8>*2 stack-items. */
/* ASM_ 0xd8 * -------- - ------------------ */
#define ASM_GETMEMBER_THIS_R 0xd9 /* [3][-0,+1] `push getmember this, ref <imm8>, $<imm8>' - Same as `ASM_GETMEMBER_THIS', but use a referenced variable `<imm8>' as class type. */
#define ASM_DELMEMBER_THIS_R 0xda /* [3][-0,+0] `delmember this, ref <imm8>, $<imm8>' - Same as `ASM_DELMEMBER_THIS', but use a referenced variable `<imm8>' as class type. */
#define ASM_SETMEMBER_THIS_R 0xdb /* [3][-1,+0] `setmember this, ref <imm8>, $<imm8>, pop' - Same as `ASM_SETMEMBER_THIS', but use a referenced variable `<imm8>' as class type. */
#define ASM_GETMEMBER_THIS_R 0xd8 /* [3][-0,+1] `push getmember this, ref <imm8>, $<imm8>' - Same as `ASM_GETMEMBER_THIS', but use a referenced variable `<imm8>' as class type. */
#define ASM_DELMEMBER_THIS_R 0xd9 /* [3][-0,+0] `delmember this, ref <imm8>, $<imm8>' - Same as `ASM_DELMEMBER_THIS', but use a referenced variable `<imm8>' as class type. */
#define ASM_SETMEMBER_THIS_R 0xda /* [3][-1,+0] `setmember this, ref <imm8>, $<imm8>, pop' - Same as `ASM_SETMEMBER_THIS', but use a referenced variable `<imm8>' as class type. */
#define ASM_SETMEMBERI_THIS_R 0xdb /* [3][-1,+0] `setmemberi this, ref <imm8>, $<imm8>, pop' - Same as `ASM_SETMEMBER_THIS_R', but does `err_cant_access_attribute_string_c()' if the member already has a value (the "i" stands for "initial"). */
#define ASM_BOUNDMEMBER_THIS_R 0xdc/* [3][-0,+1] `push boundmember this, ref <imm8>, $<imm8>' - Same as `ASM_BOUNDMEMBER_THIS', but use a referenced variable `<imm8>' as class type. */
#define ASM_CALL_EXTERN 0xdd /* [4][-n,+1] `push call extern <imm8>:<imm8>, #<imm8>' - Pop #<imm8> (second) values from the stack, pack then into a Tuple, then call an external function referenced by <imm8>:<imm8> (first). */
#define ASM_CALL_GLOBAL 0xde /* [3][-n,+1] `push call global <imm8>, #<imm8>' - Pop #<imm8> (second) values from the stack, pack then into a Tuple, then call a function in global slot <imm8> (first). */
Expand Down Expand Up @@ -1411,10 +1411,10 @@
#define ASM16_CALLATTR_THIS_C_TUPLE 0xf0d5 /* [4][-1,+1] `callattr this, const <imm16>, pop' - Pop a Tuple and lookup and call an attribute of `this', using a string in constant slot `<imm16>' (Only valid for code with the `CODE_FTHISCALL' flag set) */
#define ASM16_CALLATTR_C_SEQ 0xf0d6 /* [5][-1-n,+1] `callattr top, const <imm16>, [#<imm8>]' - Call an attribute <imm16> with a single sequence-like argument packed from the top #<imm8> stack-items. */
#define ASM16_CALLATTR_C_MAP 0xf0d7 /* [5][-1-n,+1] `callattr top, const <imm16>, {#<imm8>*2}' - Call an attribute <imm16> with a single mapping-like argument packed from the top #<imm8>*2 stack-items. */
/* ASM_ 0xf0d8 * -------- - ------------------ */
#define ASM16_GETMEMBER_THIS_R 0xf0d9/* [6][-0,+1] `push getmember this, ref <imm16>, $<imm16>' - Same as `ASM16_GETMEMBER_THIS', but use a referenced variable `<imm16>' as class type. */
#define ASM16_DELMEMBER_THIS_R 0xf0da/* [6][-0,+0] `delmember this, ref <imm16>, $<imm16>' - Same as `ASM16_DELMEMBER_THIS', but use a referenced variable `<imm16>' as class type. */
#define ASM16_SETMEMBER_THIS_R 0xf0db/* [6][-1,+0] `setmember this, ref <imm16>, $<imm16>, pop' - Same as `ASM16_SETMEMBER_THIS', but use a referenced variable `<imm16>' as class type. */
#define ASM16_GETMEMBER_THIS_R 0xf0d8/* [6][-0,+1] `push getmember this, ref <imm16>, $<imm16>' - Same as `ASM16_GETMEMBER_THIS', but use a referenced variable `<imm16>' as class type. */
#define ASM16_DELMEMBER_THIS_R 0xf0d9/* [6][-0,+0] `delmember this, ref <imm16>, $<imm16>' - Same as `ASM16_DELMEMBER_THIS', but use a referenced variable `<imm16>' as class type. */
#define ASM16_SETMEMBER_THIS_R 0xf0da/* [6][-1,+0] `setmember this, ref <imm16>, $<imm16>, pop' - Same as `ASM16_SETMEMBER_THIS', but use a referenced variable `<imm16>' as class type. */
#define ASM16_SETMEMBERI_THIS_R 0xf0db/*[6][-1,+0] `setmemberi this, ref <imm16>, $<imm16>, pop' - Same as `ASM16_SETMEMBER_THIS_R', but does `err_cant_access_attribute_string_c()' if the member already has a value (the "i" stands for "initial"). */
#define ASM16_BOUNDMEMBER_THIS_R 0xf0dc/*[6][-0,+1] `push boundmember this, ref <imm16>, $<imm16>' - Same as `ASM16_BOUNDMEMBER_THIS', but use a referenced variable `<imm16>' as class type. */
#define ASM16_CALL_EXTERN 0xf0dd /* [7][-n,+1] `push call extern <imm16>:<imm16>, #<imm8>' - Pop #<imm8> values from the stack, pack then into a Tuple, then call an external function referenced by <imm16>:<imm16>. */
#define ASM16_CALL_GLOBAL 0xf0de /* [5][-n,+1] `push call global <imm16>, #<imm8>' - Pop #<imm8> values from the stack, pack then into a Tuple, then call a function in global slot <imm16>. */
Expand Down
17 changes: 4 additions & 13 deletions include/deemon/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,10 +673,12 @@ DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DCALL DeeInstance_GetMember(/*Cl
DFUNDEF WUNUSED NONNULL((1, 2)) bool DCALL DeeInstance_BoundMember(/*Class*/ DeeTypeObject *__restrict tp_self, /*Instance*/ DeeObject *__restrict self, uint16_t addr);
DFUNDEF WUNUSED NONNULL((1, 2)) int DCALL DeeInstance_DelMember(/*Class*/ DeeTypeObject *__restrict tp_self, /*Instance*/ DeeObject *__restrict self, uint16_t addr);
DFUNDEF NONNULL((1, 2, 4)) void DCALL DeeInstance_SetMember(/*Class*/ DeeTypeObject *tp_self, /*Instance*/ DeeObject *self, uint16_t addr, DeeObject *value);
DFUNDEF WUNUSED NONNULL((1, 2, 4)) int DCALL DeeInstance_SetMemberInitial(/*Class*/ DeeTypeObject *tp_self, /*Instance*/ DeeObject *self, uint16_t addr, DeeObject *value);
DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DCALL DeeInstance_GetMemberSafe(DeeTypeObject *tp_self, DeeObject *__restrict self, uint16_t addr);
DFUNDEF WUNUSED NONNULL((1, 2)) int DCALL DeeInstance_BoundMemberSafe(DeeTypeObject *tp_self, DeeObject *__restrict self, uint16_t addr);
DFUNDEF WUNUSED NONNULL((1, 2)) int DCALL DeeInstance_DelMemberSafe(DeeTypeObject *tp_self, DeeObject *__restrict self, uint16_t addr);
DFUNDEF WUNUSED NONNULL((1, 2, 4)) int DCALL DeeInstance_SetMemberSafe(DeeTypeObject *tp_self, DeeObject *self, uint16_t addr, DeeObject *value);
DFUNDEF WUNUSED NONNULL((1, 2, 4)) int DCALL DeeInstance_SetMemberInitialSafe(DeeTypeObject *tp_self, DeeObject *self, uint16_t addr, DeeObject *value);

/* Class member access (by addr) */
DFUNDEF NONNULL((1, 3)) void DCALL DeeClass_SetMember(DeeTypeObject *self, uint16_t addr, DeeObject *value);
Expand Down Expand Up @@ -721,25 +723,14 @@ DeeClass_FindClassInstanceAttribute(DeeTypeObject *tp_invoker, DeeTypeObject *se
struct attribute_info *__restrict result,
struct attribute_lookup_rules const *__restrict rules);

/* @return: 2: Attribute isn't a basic one
/* Used to initialize attributes in deault constructor calls.
* @return: 0: Basic attribute successfully set
* @return: -1: An error occurred. */
#ifdef __INTELLISENSE__
INTDEF WUNUSED NONNULL((1, 2, 3, 4, 5)) int DCALL
INTDEF WUNUSED NONNULL((1, 2, 3, 4)) int DCALL
DeeInstance_SetBasicAttribute(struct Dee_class_desc *__restrict desc,
struct Dee_instance_desc *__restrict self,
DeeObject *this_arg,
struct Dee_class_attribute const *__restrict attr,
DeeObject *value);
#else /* __INTELLISENSE__ */
#define DeeInstance_SetBasicAttribute(desc, self, this_arg, attr, value) \
DeeInstance_SetBasicAttribute_(desc, self, attr, value)
INTDEF WUNUSED NONNULL((1, 2, 3, 4)) int
(DCALL DeeInstance_SetBasicAttribute_)(struct Dee_class_desc *__restrict desc,
struct Dee_instance_desc *__restrict self,
struct Dee_class_attribute const *__restrict attr,
DeeObject *__restrict value);
#endif /* !__INTELLISENSE__ */

/* Get/Call/Del/Set a class attribute, as acquired
* through `DeeClassDescriptor_QueryClassAttribute()'. */
Expand Down
1 change: 1 addition & 0 deletions include/deemon/compiler/assembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,7 @@ INTDEF WUNUSED int DCALL asm_gunwind(void);
#define asm_ggetmember_this_r(rid, iid) (asm_incsp(), asm_put881616(ASM_GETMEMBER_THIS_R, rid, iid))
#define asm_gdelmember_this_r(rid, iid) (asm_put881616(ASM_DELMEMBER_THIS_R, rid, iid))
#define asm_gsetmember_this_r(rid, iid) (asm_decsp(), asm_put881616(ASM_SETMEMBER_THIS_R, rid, iid))
#define asm_gsetmemberi_this_r(rid, iid) (asm_decsp(), asm_put881616(ASM_SETMEMBERI_THIS_R, rid, iid))
#define asm_gboundmember_this_r(rid, iid) (asm_incsp(), asm_put881616(ASM_BOUNDMEMBER_THIS_R, rid, iid))
#define asm_goperator(oid, n) \
(asm_subsp((n) + 1), asm_incsp(), \
Expand Down
2 changes: 2 additions & 0 deletions include/deemon/compiler/lexer.def
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,8 @@ DEF_WARNING(W_EXPECTED_CALLBACK_NAME_IN_PROPERTY, (WG_SYNTAX), WSTATE_ERROR, WAR
DEF_WARNING(W_PROPERTY_CALLBACK_ALREADY_DEFINED, (WG_SYMBOL), WSTATE_ERROR, WARNF("Property callback `%s' has already been defined", ARG(char *)))
DEF_WARNING(W_CLASS_MEMBER_TYPE_NOT_ASSIGNED, (WG_SYMBOL), WSTATE_WARN, WARNF("Explicitly typed member `%s' was not initialized", KWDNAME()))
DEF_WARNING(W_CLASS_MEMBER_TYPE_NOT_MATCHED, (WG_SYMBOL), WSTATE_WARN, WARNF("Explicitly typed member `%s' expectation was not met", KWDNAME()))
DEF_WARNING(W_CLASS_MEMBER_VARYING_IGNORED_FOR_PROPERTY, (WG_SYMBOL), WSTATE_WARN, WARNF("`varying' modifier ignored for `property %s'", KWDNAME()))
DEF_WARNING(W_CLASS_MEMBER_VARYING_IGNORED_FOR_FUNCTION, (WG_SYMBOL), WSTATE_WARN, WARNF("`varying' modifier ignored for `function %s'", KWDNAME()))
DEF_WARNING(W_DEPRECATED_PROPERTY_MODIFIER, (WG_SYNTAX), WSTATE_ERROR, WARNF("Property modifier " TOK_S " is deprecated and ignored", TOK_A))
DEF_WARNING(W_DEPRECATED_PROPERTY_NAME, (WG_SYNTAX), WSTATE_ERROR, WARNF("Property name " TOK_S " is deprecated. Use `%s' instead", TOK_A, ARG(char *)))
DEF_WARNING(W_EXPECTED_PROPERTY_NAME_AFTER_TYPE_PREFIX, (WG_SYNTAX), WSTATE_ERROR, WARNF("Expected a property callback name, but got " TOK_S, TOK_A))
Expand Down
2 changes: 1 addition & 1 deletion lib/doc.dee
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ class TypeRef: WeakRefAble {
return m_docstring;
}
}
public final member m_docstring: string | Type;
private member m_docstring: string | Type;

@@The doc node that contains a reference to @this TypeRef
public final member docnode: WeakRef with Doc;
Expand Down
14 changes: 8 additions & 6 deletions lib/rt/bytecode.dee
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,10 @@ final global ASM_CALLATTR_THIS_C = 0xd4;
final global ASM_CALLATTR_THIS_C_TUPLE = 0xd5;
final global ASM_CALLATTR_C_SEQ = 0xd6;
final global ASM_CALLATTR_C_MAP = 0xd7;
final global ASM_GETMEMBER_THIS_R = 0xd9;
final global ASM_DELMEMBER_THIS_R = 0xda;
final global ASM_SETMEMBER_THIS_R = 0xdb;
final global ASM_GETMEMBER_THIS_R = 0xd8;
final global ASM_DELMEMBER_THIS_R = 0xd9;
final global ASM_SETMEMBER_THIS_R = 0xda;
final global ASM_SETMEMBERI_THIS_R = 0xdb;
final global ASM_BOUNDMEMBER_THIS_R = 0xdc;
final global ASM_CALL_EXTERN = 0xdd;
final global ASM_CALL_GLOBAL = 0xde;
Expand Down Expand Up @@ -381,9 +382,10 @@ final global ASM16_CALLATTR_THIS_C = 0xf0d4;
final global ASM16_CALLATTR_THIS_C_TUPLE = 0xf0d5;
final global ASM16_CALLATTR_C_SEQ = 0xf0d6;
final global ASM16_CALLATTR_C_MAP = 0xf0d7;
final global ASM16_GETMEMBER_THIS_R = 0xf0d9;
final global ASM16_DELMEMBER_THIS_R = 0xf0da;
final global ASM16_SETMEMBER_THIS_R = 0xf0db;
final global ASM16_GETMEMBER_THIS_R = 0xf0d8;
final global ASM16_DELMEMBER_THIS_R = 0xf0d9;
final global ASM16_SETMEMBER_THIS_R = 0xf0da;
final global ASM16_SETMEMBERI_THIS_R = 0xf0db;
final global ASM16_BOUNDMEMBER_THIS_R = 0xf0dc;
final global ASM16_CALL_EXTERN = 0xf0dd;
final global ASM16_CALL_GLOBAL = 0xf0de;
Expand Down
1 change: 1 addition & 0 deletions src/deemon/compiler/asm/assembler.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ STATIC_ASSERT((ASM16_CALLATTR_THIS_C_TUPLE & 0xff) == ASM_CALLATTR_THIS_C_TUPLE)
STATIC_ASSERT((ASM16_GETMEMBER_THIS_R & 0xff) == ASM_GETMEMBER_THIS_R);
STATIC_ASSERT((ASM16_DELMEMBER_THIS_R & 0xff) == ASM_DELMEMBER_THIS_R);
STATIC_ASSERT((ASM16_SETMEMBER_THIS_R & 0xff) == ASM_SETMEMBER_THIS_R);
STATIC_ASSERT((ASM16_SETMEMBERI_THIS_R & 0xff) == ASM_SETMEMBERI_THIS_R);
STATIC_ASSERT((ASM16_CALL_EXTERN & 0xff) == ASM_CALL_EXTERN);
STATIC_ASSERT((ASM16_CALL_GLOBAL & 0xff) == ASM_CALL_GLOBAL);
STATIC_ASSERT((ASM16_CALL_LOCAL & 0xff) == ASM_CALL_LOCAL);
Expand Down
34 changes: 20 additions & 14 deletions src/deemon/compiler/asm/genstore.c
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,6 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
struct ast *__restrict value,
struct ast *__restrict ddi_ast,
unsigned int gflags) {
#if 1
struct symbol *class_sym, *this_sym;
struct class_attribute *attr;
int32_t symid;
Expand All @@ -652,16 +651,12 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
this_sym = sym->s_attr.a_this;
attr = sym->s_attr.a_attr;
SYMBOL_INPLACE_UNWIND_ALIAS(class_sym);
if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) {
/* TODO: Dedicated warning. */
if (ASM_WARN(W_ASM_CANNOT_WRITE_SYMBOL, sym))
goto err;
return ast_genasm(value, gflags & ASM_G_FPUSHRES);
}
if (!this_sym) {
set_class_attribute:
if (attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) {
/* Must invoke the getter callback. */
if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY)
goto fallback;
/* Must invoke the setter callback. */
if (PUSH_RESULT && ast_genasm(value, ASM_G_FPUSHRES))
goto err;
if (asm_putddi(ddi_ast))
Expand Down Expand Up @@ -724,15 +719,20 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
if (asm_putddi(ddi_ast))
goto err;
}
if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) {
/* XXX: Assert that not already bound? */
}
if (asm_gdefcmember(attr->ca_addr))
goto err; /* [result], class */
return asm_gpop(); /* [result] */
}
/* The attribute must be accessed as virtual. */

/* Check if the attribute must be accessed as virtual. */
if unlikely(asm_check_thiscall(sym, ddi_ast))
goto err;
SYMBOL_INPLACE_UNWIND_ALIAS(this_sym);
if (!(attr->ca_flag & (CLASS_ATTRIBUTE_FPRIVATE | CLASS_ATTRIBUTE_FFINAL))) {
do_virtual_access:
symid = asm_newconst((DeeObject *)attr->ca_name);
if unlikely(symid < 0)
goto err;
Expand Down Expand Up @@ -767,8 +767,11 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
}
return asm_gsetattr_const((uint16_t)symid); /* [result] */
}

/* Regular, old member variable. */
if (attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) {
if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY)
goto fallback;
/* Call the setter function of the attribute. */
if (PUSH_RESULT && ast_genasm(value, ASM_G_FPUSHRES))
goto err; /* value */
Expand Down Expand Up @@ -862,6 +865,8 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
goto set_class_attribute;
if (this_sym->s_type != SYMBOL_TYPE_THIS ||
SYMBOL_MUST_REFERENCE_THIS(this_sym)) {
if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY)
goto do_virtual_access; /* There is no `setmemberi pop, pop, $<imm8>, pop' instruction, so use fallback */
if (PUSH_RESULT) {
if (ast_genasm(value, ASM_G_FPUSHRES))
goto err; /* result */
Expand Down Expand Up @@ -897,9 +902,13 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
goto err;
if (PUSH_RESULT && asm_gdup())
goto err; /* [result], value */
if (asm_gsetmember_this_r((uint16_t)symid, attr->ca_addr))
if ((attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY)
? asm_gsetmemberi_this_r((uint16_t)symid, attr->ca_addr)
: asm_gsetmember_this_r((uint16_t)symid, attr->ca_addr))
goto err; /* [result] */
} else {
if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY)
goto do_virtual_access; /* There is no `setmemberi this, pop, $<imm8>, pop' instruction, so use fallback */
if (PUSH_RESULT) {
if (ast_genasm(value, ASM_G_FPUSHRES))
goto err; /* result */
Expand All @@ -923,9 +932,7 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
goto err; /* [result] */
}
return 0;
err:
return -1;
#else
fallback:
if (ast_genasm(value, ASM_G_FPUSHRES))
goto err;
if (asm_putddi(ddi_ast))
Expand All @@ -935,7 +942,6 @@ asm_set_cattr_symbol(struct symbol *__restrict sym,
return asm_gpop_symbol(sym, ddi_ast);
err:
return -1;
#endif
}


Expand Down
6 changes: 6 additions & 0 deletions src/deemon/compiler/asm/userdb.def
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,12 @@ INSTR("setmember", (
O4(ASM_SETMEMBER_THIS_R, FRELABS | FF0 | FF0_IMM, (THIS)(REF)(IMM16)(POP)) /* `setmember this, ref <imm8/16>, $<imm16>, pop' */
))

/* CONSTRAINTS: `setmemberi <T>, <r>, ..., <s>' */
INSTR("setmemberi", (
O4(ASM_SETMEMBERI_THIS_R, FRELABS | FF0 | FF0_IMM, (THIS)(REF)(IMM8)(POP)) /* `setmemberi this, ref <imm8/16>, $<imm8>, pop' */
O4(ASM_SETMEMBERI_THIS_R, FRELABS | FF0 | FF0_IMM, (THIS)(REF)(IMM16)(POP)) /* `setmemberi this, ref <imm8/16>, $<imm16>, pop' */
))

/* CONSTRAINTS: `reduce <s>, ...' */
INSTR("reduce", (
O2(ASM_REDUCE_MIN, FNORMAL, (TOP)(MIN)) /* `reduce top, min' */
Expand Down
Loading

0 comments on commit e4753f4

Please sign in to comment.