Re-added support for reference arguments.
Re-added support for reference arguments.
1.1 --- a/src/API.cpp Mon Nov 19 02:12:13 2012 -0800
1.2 +++ b/src/API.cpp Thu Dec 20 00:59:38 2012 -0800
1.3 @@ -289,11 +289,13 @@
1.4 case Ke::Parameter::Float:
1.5 return type->isFloat();
1.6 case Ke::Parameter::FloatRef:
1.7 - return type->isReference() && type->contained_ref()->isFloat();
1.8 + return type->isReference() && ReferenceType::cast(type)->contained()->isFloat();
1.9 case Ke::Parameter::Int:
1.10 return type->isInt32() || type->isEnum();
1.11 case Ke::Parameter::IntRef:
1.12 - return type->isReference() && (type->contained_ref()->isInt32() || type->contained_ref()->isEnum());
1.13 + return type->isReference() &&
1.14 + (ReferenceType::cast(type)->contained()->isInt32() ||
1.15 + ReferenceType::cast(type)->contained()->isEnum());
1.16 default:
1.17 return false;
1.18 }
2.1 --- a/src/Messages.tbl Mon Nov 19 02:12:13 2012 -0800
2.2 +++ b/src/Messages.tbl Thu Dec 20 00:59:38 2012 -0800
2.3 @@ -41,8 +41,7 @@
2.4 MSG(ExpectedGlobal, SyntaxError, "expected global variable or function")
2.5 MSG(ForwardNotImplemented, SyntaxError, "forward '%s' not implemented")
2.6 MSG(ForwardDeclareMismatches, SyntaxError, "declaration does not match original forward signature")
2.7 -MSG(ArrayCantBeReference, SyntaxError, "redundant reference qualifier on array parameter")
2.8 -MSG(StructCantBeReference, SyntaxError, "redundant reference qualifier on struct parameter")
2.9 +MSG(ArrayCantBeReference, SyntaxError, "this type cannot be used with a reference")
2.10 MSG(ArraySizeMustBeConstant, SyntaxError, "array size must be a constant integer")
2.11 MSG(ReferenceIsConstant, SyntaxError, "cannot assign to a constant lvalue")
2.12 MSG(MultiArrayInNative, SyntaxError, "multidimensional arrays cannot be passed to natives")
3.1 --- a/src/ObjectVisitor.h Mon Nov 19 02:12:13 2012 -0800
3.2 +++ b/src/ObjectVisitor.h Thu Dec 20 00:59:38 2012 -0800
3.3 @@ -50,6 +50,12 @@
3.4 return sizeof(Type);
3.5 }
3.6
3.7 + size_t iterateReferenceType(Object *obj) {
3.8 + iterateType(obj);
3.9 + visitField(obj, ReferenceType::offsetOfContained());
3.10 + return sizeof(ReferenceType);
3.11 + }
3.12 +
3.13 size_t iterateArrayType(Object *obj) {
3.14 iterateType(obj);
3.15 visitField(obj, ArrayType::offsetOfContained());
3.16 @@ -195,6 +201,9 @@
3.17 case MapKind_Type:
3.18 return iterateType(obj);
3.19
3.20 + case MapKind_ReferenceType:
3.21 + return iterateReferenceType(obj);
3.22 +
3.23 case MapKind_ArrayType:
3.24 return iterateArrayType(obj);
3.25
3.26 @@ -278,6 +287,9 @@
3.27 case MapKind_Type:
3.28 return sizeof(Type);
3.29
3.30 + case MapKind_ReferenceType:
3.31 + return sizeof(ReferenceType);
3.32 +
3.33 case MapKind_ArrayType:
3.34 return sizeof(ArrayType);
3.35
4.1 --- a/src/Objects.h Mon Nov 19 02:12:13 2012 -0800
4.2 +++ b/src/Objects.h Thu Dec 20 00:59:38 2012 -0800
4.3 @@ -38,6 +38,7 @@
4.4 MapKind_FunctionType,
4.5 MapKind_StructType,
4.6 MapKind_ArrayType,
4.7 + MapKind_ReferenceType,
4.8
4.9 // Represents basic data structures. When adding to this, don't forget
4.10 // to update Zone::initialize(), or you'll get a NULL crash trying to
5.1 --- a/src/TypeManager.cpp Mon Nov 19 02:12:13 2012 -0800
5.2 +++ b/src/TypeManager.cpp Thu Dec 20 00:59:38 2012 -0800
5.3 @@ -57,7 +57,7 @@
5.4 Local<Type> type(zone_);
5.5 for (unsigned i = 0; i < PrimitiveTypes_Total; i++) {
5.6 type = primitiveTypes_[i];
5.7 - if ((referenceTypes_[i] = Type::NewReference(zone_, type, false)) == NULL)
5.8 + if ((referenceTypes_[i] = ReferenceType::New(zone_, type)) == NULL)
5.9 return false;
5.10 }
5.11
5.12 @@ -84,7 +84,7 @@
5.13 return referenceTypes_[type->primitive()];
5.14
5.15 // :TODO: cache this.
5.16 - return Type::NewReference(zone_, type, isConst);
5.17 + return ReferenceType::New(zone_, type);
5.18 }
5.19
5.20 Type *
6.1 --- a/src/Types.cpp Mon Nov 19 02:12:13 2012 -0800
6.2 +++ b/src/Types.cpp Thu Dec 20 00:59:38 2012 -0800
6.3 @@ -102,16 +102,17 @@
6.4 return type;
6.5 }
6.6
6.7 -Type *
6.8 -Type::NewReference(Zone *zone, Handle<Type> contained, bool isConst)
6.9 +ReferenceType *
6.10 +ReferenceType::New(Zone *zone, Handle<Type> contained)
6.11 {
6.12 - Local<Type> type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old)));
6.13 + Local<ReferenceType> type(zone, ReferenceType::cast(zone->allocate(MapKind_ReferenceType, sizeof(ReferenceType), Heap::Tenure_Old)));
6.14 if (!type)
6.15 return NULL;
6.16
6.17 type->kind_ = REFERENCE;
6.18 type->name_ = NULL;
6.19 - type->const_ = isConst;
6.20 + type->const_ = false;
6.21 + type->contained_ = contained;
6.22 return type;
6.23 }
6.24
6.25 @@ -204,7 +205,7 @@
6.26
6.27 default:
6.28 assert(left->kind() == Type::REFERENCE);
6.29 - return Compare(left->contained_ref(), right->contained_ref());
6.30 + return Compare(ReferenceType::cast(left)->contained(), ReferenceType::cast(right)->contained());
6.31 }
6.32 }
6.33
6.34 @@ -286,7 +287,7 @@
6.35 if (type->isEnum() || type->isStruct())
6.36 return type->name()->chars();
6.37 if (type->isReference())
6.38 - type = type->contained_ref();
6.39 + type = ReferenceType::cast(type)->contained();
6.40 switch (type->primitive()) {
6.41 case PrimitiveType_Bool:
6.42 return "bool";
7.1 --- a/src/Types.h Mon Nov 19 02:12:13 2012 -0800
7.2 +++ b/src/Types.h Thu Dec 20 00:59:38 2012 -0800
7.3 @@ -104,7 +104,6 @@
7.4 static Type *NewVoid(Zone *zone);
7.5 static Type *NewUnchecked(Zone *zone);
7.6 static Type *NewPrimitive(Zone *zone, PrimitiveType type);
7.7 - static Type *NewReference(Zone *zone, Handle<Type> contained, bool isConst);
7.8 static Type *NewEnum(Zone *zone, Handle<String> name);
7.9
7.10 static bool Compare(Type *left, Type *right);
7.11 @@ -114,7 +113,8 @@
7.12 obj->is(MapKind_Type) ||
7.13 obj->is(MapKind_FunctionType) ||
7.14 obj->is(MapKind_StructType) ||
7.15 - obj->is(MapKind_ArrayType));
7.16 + obj->is(MapKind_ArrayType) ||
7.17 + obj->is(MapKind_ReferenceType));
7.18 return (Type *)obj;
7.19 }
7.20
7.21 @@ -189,10 +189,6 @@
7.22 assert(isEnum() || isStruct());
7.23 return name_;
7.24 }
7.25 - Type *contained_ref() {
7.26 - assert(false);
7.27 - return NULL;
7.28 - }
7.29
7.30 // GC Descriptor.
7.31 static inline size_t offsetOfName() {
7.32 @@ -211,6 +207,30 @@
7.33 Barriered<String> name_;
7.34 };
7.35
7.36 +class ReferenceType : public Type
7.37 +{
7.38 + public:
7.39 + static ReferenceType *New(Zone *zone, Handle<Type> contained);
7.40 +
7.41 + Type *contained() {
7.42 + return contained_;
7.43 + }
7.44 +
7.45 + // GC Descriptor.
7.46 + static inline size_t offsetOfContained() {
7.47 + return OFFSETOF(ReferenceType, contained_);
7.48 + }
7.49 + // End GC Descriptor.
7.50 +
7.51 + static ReferenceType *cast(Object *obj) {
7.52 + assert(!obj || obj->is(MapKind_ReferenceType));
7.53 + return (ReferenceType *)obj;
7.54 + }
7.55 +
7.56 + private:
7.57 + Barriered<Type> contained_;
7.58 +};
7.59 +
7.60 class ArrayType : public Type
7.61 {
7.62 public:
8.1 --- a/src/Zone.cpp Mon Nov 19 02:12:13 2012 -0800
8.2 +++ b/src/Zone.cpp Thu Dec 20 00:59:38 2012 -0800
8.3 @@ -80,6 +80,7 @@
8.4 MapKind_ArrayType,
8.5 MapKind_FunctionType,
8.6 MapKind_StructType,
8.7 + MapKind_ReferenceType,
8.8 MapKind_FixedArray,
8.9 MapKind_ByteArray,
8.10 MapKind_Code,
9.1 --- a/src/compiler/BytecodeEmitter.cpp Mon Nov 19 02:12:13 2012 -0800
9.2 +++ b/src/compiler/BytecodeEmitter.cpp Thu Dec 20 00:59:38 2012 -0800
9.3 @@ -282,6 +282,22 @@
9.4 return true;
9.5 }
9.6
9.7 +void
9.8 +BytecodeEmitter::newref(VariableSymbol *var)
9.9 +{
9.10 + recordOops();
9.11 + if (var->storage() == VariableSymbol::Local) {
9.12 + ensure(OP_VARREF_LIFO_LENGTH);
9.13 + writeOp(OP_VARREF_LIFO);
9.14 + } else {
9.15 + assert(var->storage() == VariableSymbol::Arg);
9.16 + ensure(OP_ARGREF_LIFO_LENGTH);
9.17 + writeOp(OP_ARGREF_LIFO);
9.18 + }
9.19 + writeUint32(var->slot());
9.20 + markOop(1);
9.21 +}
9.22 +
9.23 Code *
9.24 BytecodeEmitter::finish()
9.25 {
10.1 --- a/src/compiler/BytecodeEmitter.h Mon Nov 19 02:12:13 2012 -0800
10.2 +++ b/src/compiler/BytecodeEmitter.h Thu Dec 20 00:59:38 2012 -0800
10.3 @@ -513,21 +513,8 @@
10.4 writeOp(OP_UNWINDLIFO);
10.5 writeUint32(slot);
10.6 }
10.7 -#if 0
10.8 - void newref(Variable *var) {
10.9 - recordOops();
10.10 - if (var->location() == Variable::STACK) {
10.11 - ensure(OP_VARREF_LIFO_LENGTH);
10.12 - writeOp(OP_VARREF_LIFO);
10.13 - } else {
10.14 - assert(var->location() == Variable::PARAMETER);
10.15 - ensure(OP_ARGREF_LIFO_LENGTH);
10.16 - writeOp(OP_ARGREF_LIFO);
10.17 - }
10.18 - writeUint32(var->slot());
10.19 - markOop(1);
10.20 - }
10.21 -#endif
10.22 +
10.23 + void newref(VariableSymbol *var);
10.24
10.25 void slotref_lifo() {
10.26 recordOops();
11.1 --- a/src/compiler/HIR.cpp Mon Nov 19 02:12:13 2012 -0800
11.2 +++ b/src/compiler/HIR.cpp Thu Dec 20 00:59:38 2012 -0800
11.3 @@ -47,6 +47,7 @@
11.4 void visitConvert(HConvert *convert);
11.5 void visitIndex(HIndex *index);
11.6 void visitPostIncDec(HPostIncDec *hir);
11.7 + void visitToRef(HToRef *hir);
11.8
11.9 private:
11.10 Zone *zone_;
11.11 @@ -114,6 +115,26 @@
11.12 __ getglobal(sym_);
11.13 }
11.14
11.15 +bool
11.16 +HGlobal::ref(BytecodeEmitter &emitter_)
11.17 +{
11.18 + return false;
11.19 +}
11.20 +
11.21 +static inline Type *
11.22 +MaybeDereference(Type *type)
11.23 +{
11.24 + if (type->isReference())
11.25 + return ReferenceType::cast(type)->contained();
11.26 + return type;
11.27 +}
11.28 +
11.29 +HLocal::HLocal(AstNode *node, VariableSymbol *sym)
11.30 + : HLValue(node, MaybeDereference(sym->type())),
11.31 + sym_(sym)
11.32 +{
11.33 +}
11.34 +
11.35 void
11.36 HLocal::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_)
11.37 {
11.38 @@ -149,7 +170,7 @@
11.39 HLocal::store(BytecodeEmitter &emitter_)
11.40 {
11.41 if (sym_->type()->isReference()) {
11.42 - __ setref(sym_->type()->contained_ref());
11.43 + __ setref(ReferenceType::cast(sym_->type())->contained());
11.44 } else {
11.45 if (sym_->storage() == VariableSymbol::Local)
11.46 __ setlocal(sym_);
11.47 @@ -164,7 +185,7 @@
11.48 HLocal::load(BytecodeEmitter &emitter_)
11.49 {
11.50 if (sym_->type()->isReference())
11.51 - __ getref(sym_->type());
11.52 + __ getref(ReferenceType::cast(sym_->type())->contained());
11.53 else if (sym_->storage() == VariableSymbol::Local)
11.54 __ getlocal(sym_);
11.55 else if (sym_->storage() == VariableSymbol::Arg)
11.56 @@ -173,6 +194,33 @@
11.57 assert(false);
11.58 }
11.59
11.60 +bool
11.61 +HLocal::ref(BytecodeEmitter &emitter_)
11.62 +{
11.63 + if (sym_->storage() == VariableSymbol::Heap) {
11.64 + // Closing over a reference should never pass SemA.
11.65 + assert(!sym_->type()->isReference());
11.66 +
11.67 + // We don't support direct references to heap-allocated locals. This
11.68 + // should probably be banned in SemA? But for now, just assert. :TODO:
11.69 + assert(false);
11.70 + return false;
11.71 + }
11.72 +
11.73 + if (sym_->type()->isReference()) {
11.74 + if (sym_->storage() == VariableSymbol::Local)
11.75 + __ getlocal(sym_);
11.76 + else if (sym_->storage() == VariableSymbol::Arg)
11.77 + __ getarg(sym_);
11.78 + else
11.79 + assert(false);
11.80 + } else {
11.81 + __ newref(sym_);
11.82 + }
11.83 +
11.84 + return true;
11.85 +}
11.86 +
11.87 void
11.88 HIndex::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_)
11.89 {
11.90 @@ -208,6 +256,12 @@
11.91 __ getelem(type());
11.92 }
11.93
11.94 +bool
11.95 +HIndex::ref(BytecodeEmitter &emitter_)
11.96 +{
11.97 + return false;
11.98 +}
11.99 +
11.100 void
11.101 HIRTranslator::visitLocal(HLocal *local)
11.102 {
11.103 @@ -350,6 +404,20 @@
11.104 }
11.105
11.106 void
11.107 +HIRTranslator::visitToRef(HToRef *hir)
11.108 +{
11.109 + HIR *in = hir->in();
11.110 +
11.111 + // See if we can bind a direct reference.
11.112 + if (in->isLValue() && in->toLValue()->ref(emitter_))
11.113 + return;
11.114 +
11.115 + // No, we can't. So instead we create an on-stack reference.
11.116 + in->accept(this);
11.117 + __ slotref_lifo();
11.118 +}
11.119 +
11.120 +void
11.121 ke::EmitHIR(Zone *zone, BytecodeEmitter &emitter, HIR *hir)
11.122 {
11.123 HIRTranslator translator(zone, emitter);
12.1 --- a/src/compiler/HIR.h Mon Nov 19 02:12:13 2012 -0800
12.2 +++ b/src/compiler/HIR.h Thu Dec 20 00:59:38 2012 -0800
12.3 @@ -30,6 +30,7 @@
12.4 _(Integer) \
12.5 _(Float) \
12.6 _(Store) \
12.7 + _(ToRef) \
12.8 /* Special LValue types */ \
12.9 _(Index) \
12.10 _(Local) \
12.11 @@ -46,6 +47,7 @@
12.12
12.13 class HIRVisitor;
12.14 class AstNode;
12.15 +class HLValue;
12.16
12.17 class HIR : public PoolObject
12.18 {
12.19 @@ -78,6 +80,22 @@
12.20 virtual HOp op() const = 0;
12.21 virtual void accept(HIRVisitor *visitor) = 0;
12.22
12.23 +#define _(name) \
12.24 + bool is##name() const { \
12.25 + return op() == k##name; \
12.26 + } \
12.27 + H##name *to##name() { \
12.28 + assert(is##name()); \
12.29 + return reinterpret_cast<H##name *>(this); \
12.30 + }
12.31 + HIR_OPS(_)
12.32 +#undef _
12.33 +
12.34 + HLValue *toLValue() {
12.35 + assert(isLValue());
12.36 + return reinterpret_cast<HLValue *>(this);
12.37 + }
12.38 +
12.39 private:
12.40 AstNode *node_;
12.41 ScopedRoot<Type> type_;
12.42 @@ -173,6 +191,10 @@
12.43 virtual void store(BytecodeEmitter &emitter_) = 0;
12.44 virtual void load(BytecodeEmitter &emitter_) = 0;
12.45
12.46 + // Try to make a direct reference, if semantics allow it. False if the
12.47 + // caller must allocate a reference to the stack.
12.48 + virtual bool ref(BytecodeEmitter &emitter_) = 0;
12.49 +
12.50 bool isLValue() const {
12.51 return true;
12.52 }
12.53 @@ -181,11 +203,7 @@
12.54 class HLocal : public HLValue
12.55 {
12.56 public:
12.57 - HLocal(AstNode *node, VariableSymbol *sym)
12.58 - : HLValue(node, sym->type()),
12.59 - sym_(sym)
12.60 - {
12.61 - }
12.62 + HLocal(AstNode *node, VariableSymbol *sym);
12.63
12.64 DEFINE_HIR(Local);
12.65
12.66 @@ -195,6 +213,11 @@
12.67 void swapAndPick(BytecodeEmitter &emitter_);
12.68 void store(BytecodeEmitter &emitter_);
12.69 void load(BytecodeEmitter &emitter_);
12.70 + bool ref(BytecodeEmitter &emitter_);
12.71 +
12.72 + VariableSymbol *sym() const {
12.73 + return sym_;
12.74 + }
12.75
12.76 private:
12.77 VariableSymbol *sym_;
12.78 @@ -217,6 +240,7 @@
12.79 void swapAndPick(BytecodeEmitter &emitter_);
12.80 void store(BytecodeEmitter &emitter_);
12.81 void load(BytecodeEmitter &emitter_);
12.82 + bool ref(BytecodeEmitter &emitter_);
12.83
12.84 private:
12.85 HIR *left_;
12.86 @@ -239,6 +263,7 @@
12.87 void swapAndPick(BytecodeEmitter &emitter_);
12.88 void store(BytecodeEmitter &emitter_);
12.89 void load(BytecodeEmitter &emitter_);
12.90 + bool ref(BytecodeEmitter &emitter_);
12.91
12.92 Symbol *sym() const {
12.93 return sym_;
12.94 @@ -384,6 +409,25 @@
12.95 HIRList *args_;
12.96 };
12.97
12.98 +class HToRef : public HIR
12.99 +{
12.100 + public:
12.101 + HToRef(AstNode *node, Type *type, HIR *in)
12.102 + : HIR(node, type),
12.103 + in_(in)
12.104 + {
12.105 + }
12.106 +
12.107 + DEFINE_HIR(ToRef);
12.108 +
12.109 + HIR *in() const {
12.110 + return in_;
12.111 + }
12.112 +
12.113 + private:
12.114 + HIR *in_;
12.115 +};
12.116 +
12.117 #undef DEFINE_HIR
12.118
12.119 void EmitHIR(Zone *zone, BytecodeEmitter &emitter, HIR *hir);
13.1 --- a/src/compiler/NameBinding.cpp Mon Nov 19 02:12:13 2012 -0800
13.2 +++ b/src/compiler/NameBinding.cpp Thu Dec 20 00:59:38 2012 -0800
13.3 @@ -818,6 +818,15 @@
13.4 }
13.5 }
13.6
13.7 + if (param->reference()) {
13.8 + if (!type->isPod()) {
13.9 + cc_.reportError(param->pos(), Message_ArrayCantBeReference);
13.10 + return false;
13.11 + }
13.12 + if ((type = zone_->types()->getReference(type, false)) == NULL)
13.13 + return false;
13.14 + }
13.15 +
13.16 params->set(zone_, i, type);
13.17 param->sym()->setType(type);
13.18 }
14.1 --- a/src/compiler/SemanticAnalysis.cpp Mon Nov 19 02:12:13 2012 -0800
14.2 +++ b/src/compiler/SemanticAnalysis.cpp Thu Dec 20 00:59:38 2012 -0800
14.3 @@ -516,16 +516,23 @@
14.4 }
14.5 }
14.6
14.7 - // When we start overloading, coercion will have to happen later.
14.8 - arg = coerce(arg, actual, Coerce_Arg);
14.9 - if (!arg)
14.10 - return;
14.11 + if (actual->isReference()) {
14.12 + // If the actual is a reference, the coercion rules are much
14.13 + // stricter; the contained types must be identical.
14.14 + // :TODO: make this not an assert
14.15 + assert(ReferenceType::cast(actual)->contained() == arg->type());
14.16 + } else {
14.17 + // When we start overloading, coercion will have to happen later.
14.18 + arg = coerce(arg, actual, Coerce_Arg);
14.19 + if (!arg)
14.20 + return;
14.21 + }
14.22
14.23 mustComputeReference = actual->isReference() && !given->isReference();
14.24 }
14.25
14.26 - // :TODO: handle mustComputeReference
14.27 - assert(!mustComputeReference);
14.28 + if (mustComputeReference)
14.29 + arg = new (pool_) HToRef(arg->node(), arg->type(), arg);
14.30
14.31 if (!args->append(arg))
14.32 return;
14.33 @@ -640,6 +647,7 @@
14.34 return l;
14.35 }
14.36
14.37 + // :TODO: remove
14.38 assert(l == r);
14.39
14.40 return left->type();
14.41 @@ -741,8 +749,6 @@
14.42 return NULL;
14.43 return hir;
14.44 }
14.45 -
14.46 - assert(from == to);
14.47
14.48 return hir;
14.49 }
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/tests/basic/ref-arg.out Thu Dec 20 00:59:38 2012 -0800
15.3 @@ -0,0 +1,1 @@
15.4 +122
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/tests/basic/ref-arg.sp Thu Dec 20 00:59:38 2012 -0800
16.3 @@ -0,0 +1,20 @@
16.4 +native PrintNum(b);
16.5 +
16.6 +f(&a)
16.7 +{
16.8 + for (new i = 0; i < 10; i = i + 1)
16.9 + a = a + i;
16.10 + return 0;
16.11 +}
16.12 +
16.13 +g(&b)
16.14 +{
16.15 + return f(b);
16.16 +}
16.17 +
16.18 +public main()
16.19 +{
16.20 + new b = 77;
16.21 + g(b);
16.22 + PrintNum(b);
16.23 +}
17.1 --- a/tests/prototype/ref-arg.sp Mon Nov 19 02:12:13 2012 -0800
17.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
17.3 @@ -1,18 +0,0 @@
17.4 -f(&a)
17.5 -{
17.6 - for (new i = 0; i < 10; i = i + 1)
17.7 - a = a + i;
17.8 - return 0;
17.9 -}
17.10 -
17.11 -g(&b)
17.12 -{
17.13 - return f(b);
17.14 -}
17.15 -
17.16 -main()
17.17 -{
17.18 - new b = 77;
17.19 - g(b);
17.20 - return b;
17.21 -}