RefTypesImpl.mesa
Copyright Ó 1990 by Xerox Corporation. All rights reserved.
Sturgis, September 5, 1989 9:42:59 am PDT
Last changed by Theimer on June 1, 1989 3:19:17 pm PDT
Hopcroft July 26, 1989 10:41:56 am PDT
Last tweaked by Mike Spreitzer on January 9, 1992 11:09 am PST
Sturgis: November 28, 1988 11:10:39 am PST
DIRECTORY
CCTypes USING[ApplyOperand, BinaryTargetTypes, BreakPrintType, CCError, CCErrorCase, CCTypeProcs, CheckConformance, CheckFamilyInclusion, CoerceToType, ConformanceCheck, ContainsVariance, CreateCedarType, GetGroundTypeClass, GetIndirectType, GetNilRefType, GetNodeType, GetNVariants, GetProcDataFromGroundType, GetProcDataFromType, GetRefAnyType, GetRTargetType, GetTargetTypeOfIndirect, Index, IsAnIndirect, IsASingleton, Load, LR, Operand, Operator, SelectIdField, UnaryOp],
CedarCode USING[CodeToExtractField, CodeToLoadContentsOfAMNode, CodeToDoUnaryOp, CodeToLoadThroughIndirect, CodeToStoreUnpopped, ConcatCode, CreateCedarNode, ExtractFieldFromNode, ForceNodeIn, GetDataFromNode, GetTypeOfNode, LoadThroughIndirectNode, OperationsBody, SelectFieldFromNode, ShowNode, StoreThroughIndirectNode],
CedarOtherPureTypes USING[CreateParseTreeNode],
CirioSyntacticOperations USING[ParseTree],
CirioTypes USING[BasicTypeInfo, Code, CompilerContext, Mem, Node, Type, TypedCode, TypeClass],
IO,
PointerTypes USING [CreatePointerType],
RefTypes USING[RefNodeInfo],
Rope;
RefTypesImpl: CEDAR PROGRAM
IMPORTS CCTypes, CedarCode, CedarOtherPureTypes, IO, PointerTypes, Rope
EXPORTS RefTypes
= BEGIN
CC: TYPE = CirioTypes.CompilerContext;
BasicTypeInfo: TYPE = CirioTypes.BasicTypeInfo;
Type: TYPE = CirioTypes.Type;
TypedCode: TYPE = CirioTypes.TypedCode;
Code: TYPE = CirioTypes.Code;
Mem: TYPE = CirioTypes.Mem;
Node: TYPE = CirioTypes.Node;
CCE: ERROR[case: CCTypes.CCErrorCase, msg: Rope.ROPENIL] ← CCTypes.CCError;
REF types present several problems because such constructors as REF VariantRecord do not produce the types that one would initially expect. We offer a brief discourse on how the following types work:
VariantRecord
ANY
Sequence
REF VariantRecord
REF ANY
REF Sequence
REF REF VariantRecord
REF REF ANY
REF REF Sequence
In this list of constructors, we use VariantRecord as shorthand for any record type expression containing a variant record construction, and we use Sequence as shorthand for any record type expression containing a SEQUENCE. Let VR stand for any of the type expressions: ANY, Sequence, and VariantRecord.
VR denotes the obvious union type. (Let this type be T, a collection of types t B T.)
REF VR denotes a union type, {Indirect TO RECORD[typeIndex: TypeIndex, body: t] | t B T}, with the constraint that if @[i, b] B REF VR, then i is an index for the type of b. Note that REF VR does not denote an Indirect To Union type, but rather a union of Indirect types. (I use Indirect as an unloaded word for pointer.)
REF REF VR denotes an indirect type, specifically, an indirect to a union type:
Indirect To {Indirect TO RECORD[typeIndex: TypeIndex, body: t] | t B T}.
We can load through a REF VR. The result is that some record belonging to VR is (virtually) placed on the stack. This is implemented by the load through indirect code in the VariantRecordsImpl module.
We cannot store through a REF VR. (Because REF VR is a union type, and the store would have to be type safe for all possible elements of REF VR. The actual Cedar compiler treats this a unsafe.) (It is possible to initialize the target of a REF VR, this is in effect a store through the Indirect TO VR, i.e., an indirect to a union type.)
If we perform a field selection on a REF VR, there are three possibilities: 1) all components of the union have target types which accept the field selection using the same implementation, 2) some, but not all, of the targets of components of the union accept the field selection (or perhaps all accept the field selection, but with differing implementations), and 3) none of the targets of components of the union accept the field selection In case 1 we emit the field selection operation. In cases 2 and 3 a Cedar compiler would report a type error. We report a type error in case 3, while in case 2 we bundle the value into a Node, and let the run time figure out what to do. Further, in case 1, sometimes the resulting type is a union and sometimes not. If the selected field includes the variable part of the record constructor VR, then the resulting type is an appropriate union of indirect types. If the selected field does not include the variable part of the record constructor, then we get an indirect type.
Finally, a REF REF VR is an indirect to a union type, not a union of indirects.
We define the following four type families:
RefTargets
This is a collection of types. A union type (the target of a REF ANY), together with various levels of discrimination. This first level of discrimination will be a type. Subsequent levels depend on the first level type. If the first level type is a variant record, then subsequent levels are those appropriate to the variant record.
IndirectRefTargets
These are indirects to the various RefTargets types.
Refs
This is a collection of types. Each is an indirect, or union of indirects, to fully discriminated types among RefTargets. i.e., each is an indirect, or union of indirects, from among the IndirectRefTargets. Further, while REF ANY and REF VR are union types, REF REF ANY and REF REF VR are indirects to union types, rather than unions of indirects. If T is a union type, then REF T is a union of indirects if GetNVariants[T] # 0, otherwise it is an indirect to a union. [This will be modified when we add Sequence types, which have not been added as of December 15, 1988 9:30:16 am PST.]
There are three classes of Ref types: REF ANY, REF T, and NIL REF. See comments in RefTypes.mesa. The REF ANY class is implemented as a REF T class, where T is the REF ANY Target type.
IndirectRefs
These are vanilla indirects to Ref types. The target type is sometimes a union, depending on the Ref.
We begin with RefTargets
We could proceed as we do in the VariantRecords module: define a Struct record that describes the whole family of types, then define an Info record that describes a specific type in the family. However, there is only one family, hence there is no need for a Struct. Further, we need not keep around the list of modifiers (such as we do in VariantRecords.VRInfo) because they will be embedded in the specific target type.
We choose not to keep an array of the possible variants, but build them on demand.
WARNING: At some point we have to add the code to (1) treat REF ANY TARGET as a value to be bundled up into a node whenever any operation approaches, and (2) be prepared to try field references through an intermediate node. These should follow from the default coercion mechanisms invoked by Operand etc.
We avoid setting containsVariance at creation. Otherwise the following nasty loop occurs: someone asks if rcd type R contains variance. That question results in asking that question for each field, and in particular, creating each field type. Suppose one of these field types is REF R. Then we end up creating REF R, hence creating a RefTargetType for R. If we ask about the variance for R during that operation, we have a cycle. But we must ask about the variance for R in order to set the containsVariance field.
RefTargetTypeInfo: TYPE = REF RefTargetTypeInfoBody;
RefTargetTypeInfoBody: TYPE = RECORD[
containsVarianceIsKnown: RTTCVKnown,
containsVariance: BOOLEAN,
self: Type,
indirect: Type,
bodyType: Type, -- NIL for a REF ANY target type, T for a REF T target.
codeForBodyType: INT];
RTTCVKnown: TYPE = {no, deciding, yes};
CreateRefTargetType: PROC[bodyType: Type, codeForBodyType: INT, cc: CC] RETURNS[Type] =
BEGIN
rttInfo: RefTargetTypeInfo ← NEW[RefTargetTypeInfoBody←[
no,
FALSE, -- any value will do, it will be ignored
NIL,
NIL,
bodyType,
codeForBodyType]];
type: Type ← rttInfo.self ← CCTypes.CreateCedarType[$refTargetType, RefTargetTypeCCTypeProcs, IndirectRefTargetTypeCCTypeProcs, cc, rttInfo];
RETURN[type];
END;
SetRefTargetTypeContainsVariance: PROC[rttInfo: RefTargetTypeInfo, cc: CC] =
BEGIN
SELECT rttInfo.containsVarianceIsKnown FROM
no =>
BEGIN
rttInfo.containsVarianceIsKnown ← deciding;
rttInfo.containsVariance ← (rttInfo.bodyType = NIL) OR CCTypes.ContainsVariance[rttInfo.bodyType, cc];
rttInfo.containsVarianceIsKnown ← yes;
END;
deciding => CCE[cirioError];
yes => RETURN;
ENDCASE => CCE[cirioError];
END;
RefTargetTypeCCTypeProcs: REF CCTypes.CCTypeProcs ← NEW[CCTypes.CCTypeProcs ←[
checkConformance: RTTCCTypesCheckConformance,
checkFamilyInclusion: RTTCCTypesCheckFamilyInclusion,
isASingleton: RTTCCTypesIsASingleton,
getNVariants: RTTCCTypesGetNVariants]];
RTTCCTypesCheckConformance: PROC[valType, varType: Type, cc: CC, procData: REF ANY] RETURNS[CCTypes.ConformanceCheck] =
BEGIN
valInfo: RefTargetTypeInfo ← NARROW[procData];
IF valInfo.containsVarianceIsKnown # yes THEN SetRefTargetTypeContainsVariance[valInfo, cc];
WITH CCTypes.GetProcDataFromGroundType[varType, cc] SELECT FROM
varInfo: RefTargetTypeInfo =>
BEGIN
IF varInfo.containsVarianceIsKnown # yes THEN SetRefTargetTypeContainsVariance[varInfo, cc];
IF varInfo.bodyType = NIL THEN RETURN[yes];
RETURN[CCTypes.CheckConformance[valInfo.bodyType, varInfo.bodyType, cc]];
END;
ENDCASE => RETURN[no];
END;
RTTCCTypesCheckFamilyInclusion: PROC[valType, varType: Type, cc: CC, procData: REF ANY] RETURNS[BOOLEAN] =
BEGIN
valInfo: RefTargetTypeInfo ← NARROW[procData];
IF valInfo.containsVarianceIsKnown # yes THEN SetRefTargetTypeContainsVariance[valInfo, cc];
WITH CCTypes.GetProcDataFromGroundType[varType, cc] SELECT FROM
varInfo: RefTargetTypeInfo =>
BEGIN
IF varInfo.containsVarianceIsKnown # yes THEN SetRefTargetTypeContainsVariance[varInfo, cc];
IF varInfo.bodyType = NIL THEN RETURN[TRUE];
IF valInfo.codeForBodyType # varInfo.codeForBodyType THEN RETURN[FALSE];
IF NOT CCTypes.CheckFamilyInclusion[valInfo.bodyType, varInfo.bodyType, cc] THEN CCE[cirioError, "ref target types found with equal typecodes and unequal body types"]; -- if the body type codes agree then the families should bloody well be equal.
RETURN[TRUE];
END;
ENDCASE => RETURN[FALSE];
END;
RTTCCTypesIsASingleton: PROC[type: Type, cc: CC, procData: REF ANY] RETURNS[BOOLEAN] =
BEGIN
valInfo: RefTargetTypeInfo ← NARROW[procData];
IF valInfo.bodyType = NIL THEN RETURN[FALSE]; -- RTT(ANY)
RETURN[CCTypes.IsASingleton[valInfo.bodyType, cc]];
END;
This will blow up for a REF ANY, what should we do? What should be the correct anwer? Better yet, how can we prevent anyone from calling this?
RTTCCTypesGetNVariants: PROC[type: Type, cc: CC, procData: REF ANY] RETURNS[INT] =
BEGIN
info: RefTargetTypeInfo ← NARROW[procData];
IF info.containsVarianceIsKnown # yes THEN SetRefTargetTypeContainsVariance[info, cc];
RETURN[CCTypes.GetNVariants[info.bodyType, cc]];
END;
We continue with Indirect Ref Targets
Warning: At some point I have to come to grips with both the coerce mechansim (that inserts needed default dereferences and unbracketing), and the node mechanism (that packages everything up into nodes for run time examination (when we are presented with a ref any target type). That is, if an operation can be applied to the target type, then we must generate code to get us to a value of that targt type. Further, when it might be applied to a target type, then we bundle up into nodes.
IndirectRefTargetTypeCCTypeProcs: REF CCTypes.CCTypeProcs ← NEW[CCTypes.CCTypeProcs ←[
store: IRTTCCTypesStore,
load: IRTTCCTypesLoad]];
The code for load and store seems to be common to a lot of types. How can we make use of this fact?
IRTTCCTypesStore: PROC[value: TypedCode, indirect: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
info: RefTargetTypeInfo ← NARROW[procData];
IF info.containsVariance THEN CCE[operation, "attempt to store into a variant record field"]; -- client error, attempt to store into a (possibly nested) variant record field or target of a REF ANY. (We shall eventually have to allow this for initialization.)
so it is ok to do it
BEGIN
code: Code ← CedarCode.ConcatCode[
indirect.code,
CedarCode.ConcatCode[
value.code,
CedarCode.CodeToStoreUnpopped[indirect.type, value.type]]];
RETURN[[code, value.type]];
END;
END;
It is legal to load through an indirect to a union type. What we must prevent is loading through a union of indirects, i.e., a REF type.
IRTTCCTypesLoad: PROC[indirect: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
code: Code ← CedarCode.ConcatCode[
indirect.code,
CedarCode.CodeToLoadThroughIndirect[indirect.type]];
type: Type ← CCTypes.GetRTargetType[indirect.type, cc];
RETURN[[code, type]];
END;
Now we can address REF types themselves.
RefInfo: TYPE = REF RefInfoBody;
RefInfoBody: TYPE = RECORD[
made: BOOLFALSE,
When made is FALSE, we don't yet know what this is a REF to.
target: Type, -- target = NIL is used for NIL REF.
bti: BasicTypeInfo];
The target is the RefTargetType to which the Ref implicitly points
Note: ClientTarget is frequently called Body in the code in this module
CreateRefAnyType: PUBLIC PROC[cc: CC, bti: BasicTypeInfo] RETURNS[Type] = {
nominal: Type ← CCTypes.GetRefAnyType[cc];
IF nominal # NIL THEN RETURN[nominal];
{refInfo: RefInfo ← NEW[RefInfoBody←[TRUE, CreateRefTargetType[NIL, 0, cc], bti]]; -- the zero code here is meaningless
type: Type ← CCTypes.CreateCedarType[$refAny, RefTypeCCTypeProcs, IndirectRefTypeCCTypeProcs, cc, refInfo];
RETURN[type]}};
CreateRefType: PUBLIC PROC[cc: CC, bti: BasicTypeInfo] RETURNS[Type] = {
refInfo: RefInfo ← NEW[RefInfoBody←[FALSE, NIL, bti]];
type: Type ← CCTypes.CreateCedarType[$ref, RefTypeCCTypeProcs, IndirectRefTypeCCTypeProcs, cc, refInfo];
RETURN[type]};
SetReferent: PUBLIC PROC [refType, clientTargetType: Type, codeForClientTargetType: INT, cc: CC] ~ {
refInfo: RefInfo ~ NARROW[CCTypes.GetProcDataFromType[refType]];
IF refInfo.made THEN CCE[cirioError, "Re-SetReferent"];
refInfo.target ← CreateRefTargetType[clientTargetType, codeForClientTargetType, cc];
refInfo.made ← TRUE;
RETURN};
CreateNilRefType: PUBLIC PROC[cc: CC] RETURNS[Type] =
BEGIN
nominal: Type ← CCTypes.GetNilRefType[cc];
IF nominal # NIL THEN RETURN[nominal];
BEGIN
refInfo: RefInfo ← NEW[RefInfoBody←[TRUE, NIL]];
type: Type ← CCTypes.CreateCedarType[$nilRef, NilRefTypeCCTypeProcs, IndirectNilRefTypeCCTypeProcs, cc, refInfo];
RETURN[type];
END;
END;
RefTypeCCTypeProcs: REF CCTypes.CCTypeProcs ← NEW[CCTypes.CCTypeProcs ←[
checkConformance: RefCCTypesCheckConformance,
binaryOperandTypes: RefCCTypesBinaryOperandTypes,
getRTargetType: RefCCTypesGetRTargetType,
operand: RefCCTypesOperand,
indexOperand: RefCCTypesIndexOperand,
coerceToType: RefCCTypesCoerceToType,
unaryOp: RefCCTypesUnaryOp,
selectIdField: RefCCTypesSelectIdField,
index: RefCCTypesIndex,
printType: RefCCTypesPrintType]];
RefCCTypesCheckConformance: PROC[valType, varType: Type, cc: CC, procData: REF ANY] RETURNS[CCTypes.ConformanceCheck] =
BEGIN
valInfo: RefInfo ← NARROW[procData];
WITH CCTypes.GetProcDataFromGroundType[varType, cc] SELECT FROM
varInfo: RefInfo =>
BEGIN
IF varInfo.target = NIL THEN RETURN[no]; -- var is NIL REF, we are not.
RETURN[CCTypes.CheckConformance[CCTypes.GetIndirectType[valInfo.target], CCTypes.GetIndirectType[varInfo.target], cc]];
END;
ENDCASE => RETURN[no];
END;
RefCCTypesBinaryOperandTypes: PROC[op: CCTypes.Operator, left, right: Type, cc: CC, procData: REF ANY] RETURNS[CCTypes.BinaryTargetTypes] =
BEGIN
leftInfo: RefInfo ← NARROW[procData];
leftTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[leftInfo.target, cc]];
SELECT op FROM
$assign =>
someone is trying to store through the REF
arrange to give him an indirect to the body
RETURN[[CCTypes.GetIndirectType[leftTargetTypeInfo.bodyType], leftTargetTypeInfo.bodyType]];
$eq, $ne, $lt, $gt, $le, $ge => RETURN [[left, left]];
ENDCASE => CCE[cirioError];
END;
Someone is trying to do an assignment through a REF and needs a (tentative) target type for the compilation of the right hand side. Lets give him the client target type.
RefCCTypesGetRTargetType: PROC[type: Type, cc: CC, procData: REF ANY] RETURNS[Type] =
BEGIN
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
RETURN[refTargetTypeInfo.bodyType];
END;
RefCCTypesOperand: PROC[op: CCTypes.Operator, lr: CCTypes.LR, tc: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
IF refTargetTypeInfo.bodyType = NIL THEN -- we are dealing with a REF ANY
True Cedar would reject this situation
We package everything up into nodes and try again at run time
RETURN[CCTypes.CoerceToType[CCTypes.GetNodeType[cc], tc, cc]]
ELSE -- we are not dealing with a REF ANY
SELECT op FROM
$selectId, $uparrow, $index, $leftSideuparrow => RETURN[tc];
$dot, $extractId, $apply => -- try dereferencing first
BEGIN
tc1: TypedCode ← CCTypes.UnaryOp[$uparrow, tc, cc];
RETURN[CCTypes.Operand[op, lr, tc1, cc]];
END;
$eq, $ne, $lt, $gt, $le, $ge => RETURN [tc];
ENDCASE => CCE[operation]; -- client error, invalid operation
END;
RefCCTypesIndexOperand: PROC[operatorType: Type, operand: CirioSyntacticOperations.ParseTree, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
IF refTargetTypeInfo.bodyType = NIL THEN -- we are dealing with a REF ANY
True Cedar would reject this situation
generate code to package the operand into a node. This will cause the caller to package the operator into a node and try again at run time.
BEGIN
node: Node ← CedarOtherPureTypes.CreateParseTreeNode[operand, cc];
code: CirioTypes.Code ← CedarCode.CodeToLoadContentsOfAMNode[node];
type: Type ← CCTypes.GetNodeType[cc];
RETURN[[code, type]];
END
ELSE -- we are not dealing with a REF ANY
treat the operand the same as would be done for an ApplyOperand of the ref target type
RETURN[CCTypes.ApplyOperand[refTargetTypeInfo.bodyType, operand, cc]];
END;
By the time we get here, we know that we do not conform
So we try handing out an indirect to the body of the target
This is exactly what RefCCTypesBinaryOperandTypes is expecting us to do when we are the left hand side of an assignment.
RefCCTypesCoerceToType: PROC[targetType: Type, tc: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
bodyType: Type ← refTargetTypeInfo.bodyType;
IF bodyType = NIL THEN
we are a ref any. We can not be coerced to any reasonable type
Moreover, the nominal mechanism would produce a NIL for a type at a later stage and this would be very bad. (I could fix that by not using a NIL at refTargetTypeInfo.bodyType to represent REF ANY.)
So we just generate a type error.
CCE[typeConformity]
ELSE
BEGIN
code1: Code ← CedarCode.ConcatCode[
tc.code,
CedarCode.CodeToExtractField["&indirectToBody", tc.type]];
type1: Type ← CCTypes.GetIndirectType[bodyType];
RETURN[CCTypes.CoerceToType[targetType, [code1, type1], cc]];
END;
END;
RefCCTypesUnaryOp: PROC[op: CCTypes.Operator, arg: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
bodyType: Type ← refTargetTypeInfo.bodyType;
code1: Code ← CedarCode.CodeToExtractField["&indirectToBody", arg.type];
SELECT op FROM
$leftSideuparrow =>
BEGIN
code: Code ← CedarCode.ConcatCode[
arg.code,
code1];
type: Type ← CCTypes.GetIndirectType[bodyType];
RETURN[[code, type]];
END;
$uparrow =>
BEGIN
code2: Code ← CedarCode.CodeToLoadThroughIndirect[CCTypes.GetIndirectType[bodyType]];
code: Code ← CedarCode.ConcatCode[
arg.code,
CedarCode.ConcatCode[
code1,
code2]];
RETURN[[code, bodyType]];
END;
ENDCASE => CCE[typeConformity]; -- client type error
END;
hopefully, the id will be recognized by the body
RefCCTypesSelectIdField: PROC[id: Rope.ROPE, fieldIndirectContext: Type, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
bodyType: Type ← refTargetTypeInfo.bodyType;
code1: Code ← CedarCode.CodeToExtractField["&indirectToBody", fieldIndirectContext];
type1: Type ← CCTypes.GetIndirectType[bodyType];
tc2: TypedCode ← CCTypes.SelectIdField[id, type1, cc];
code: Code ← CedarCode.ConcatCode[code1, tc2.code];
RETURN[[code, tc2.type]];
END;
RefCCTypesIndex: PROC[operator: TypedCode, operand: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
bodyType: Type ← refTargetTypeInfo.bodyType;
code1: Code ← CedarCode.ConcatCode[
operator.code,
CedarCode.CodeToExtractField["&indirectToBody", operator.type]];
type1: Type ← CCTypes.GetIndirectType[bodyType];
tc1: TypedCode ← [code1, type1];
RETURN[CCTypes.Index[tc1, operand, cc]];
END;
RefCCTypesPrintType: PROC [to: IO.STREAM, type: Type, printDepth: INT, printWidth: INT, cc: CC, procData: REF ANY] = {
refTypeInfo: RefInfo ← NARROW[procData];
refTargetTypeInfo: RefTargetTypeInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refTypeInfo.target, cc]];
bodyType: Type ← refTargetTypeInfo.bodyType;
refTypeClass: CirioTypes.TypeClass ← CCTypes.GetGroundTypeClass[type, cc];
IF refTypeClass = $refAny
THEN to.PutRope["REF ANY"]
ELSE {
to.PutRope["REF"];
CCTypes.BreakPrintType[to, bodyType, printDepth-1, printWidth, cc, " "];
};
};
NilRefTypeCCTypeProcs: REF CCTypes.CCTypeProcs ← NEW[CCTypes.CCTypeProcs ←[
checkConformance: NilRefCCTypesCheckConformance]];
NilRefCCTypesCheckConformance: PROC[valType, varType: Type, cc: CC, procData: REF ANY] RETURNS[CCTypes.ConformanceCheck] =
BEGIN
valInfo: RefInfo ← NARROW[procData];
WITH CCTypes.GetProcDataFromGroundType[varType, cc] SELECT FROM
varInfo: RefInfo =>
RETURN[yes]; -- NIL REF conforms to all REF types.
ENDCASE => RETURN[no];
END;
IndirectRefTypeCCTypeProcs: REF CCTypes.CCTypeProcs ← NEW[CCTypes.CCTypeProcs ←[
createIndirectNode: RefCreateIndirect,
getBitSize: RefBitSize,
operand: IRefCCTypesOperand,
unaryOp: IRefCCTypesUnaryOp,
store: IRefCCTypesStore,
load: IRefCCTypesLoad,
printType: RefCCTypesPrintType]];
IndirectNilRefTypeCCTypeProcs: REF CCTypes.CCTypeProcs ← NEW[CCTypes.CCTypeProcs ←[
store: INilRefCCTypesStore,
load: INilRefCCTypesLoad]];
The code for load and store seems to be common to a lot of types. How can we make use of this fact?
RefCreateIndirect: PROC [cc: CC, procData: REF ANY, indirectType, targetType: Type, mem: Mem] RETURNS [Node] ~ {
refInfo: RefInfo ← NARROW[procData];
IF refInfo.bti=NIL THEN CCE[cirioError, "CreateIndirect[bti-less Type]"];
RETURN refInfo.bti.createIndirectNode[refInfo.bti, cc, indirectType, targetType, mem]};
RefBitSize: PROC[indirectType, targetType: Type, cc: CC, procData: REF ANY] RETURNS[CARD] ~ {
refInfo: RefInfo ← NARROW[procData];
IF refInfo.bti=NIL THEN CCE[cirioError, "GetBitSize[bti-less Type]"];
RETURN refInfo.bti.getBitSize[refInfo.bti, cc, indirectType, targetType]};
IRefCCTypesOperand: PROC[op: CCTypes.Operator, lr: CCTypes.LR, tc: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
SELECT op FROM
$selectId, $index, $leftSideuparrow => -- try dereferencing first
BEGIN
tc1: TypedCode ← CCTypes.Load[tc, cc];
RETURN[CCTypes.Operand[op, lr, tc1, cc]];
END;
$address => RETURN[tc];
ENDCASE => CCE[operation]; -- client error, invalid operation
END;
note: this is identical to the code for DefaultIndirect. How might we safely take advantage of this?
IRefCCTypesUnaryOp: PROC[op: CCTypes.Operator, arg: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
SELECT op FROM
$address =>
BEGIN
code: CirioTypes.Code ← CedarCode.ConcatCode[
arg.code,
CedarCode.CodeToDoUnaryOp[op, arg.type]];
ptrType: Type ← PointerTypes.CreatePointerType[CCTypes.GetTargetTypeOfIndirect[arg.type], cc, NIL--same reasons as always (see default)--];
RETURN [[code, ptrType]];
END;
ENDCASE => CCE[cirioError];
END;
IRefCCTypesStore: PROC[value: TypedCode, indirect: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
code: Code ← CedarCode.ConcatCode[
indirect.code,
CedarCode.ConcatCode[
value.code,
CedarCode.CodeToStoreUnpopped[indirect.type, value.type]]];
RETURN[[code, value.type]];
END;
IRefCCTypesLoad: PROC[indirect: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
code: Code ← CedarCode.ConcatCode[
indirect.code,
CedarCode.CodeToLoadThroughIndirect[indirect.type]];
type: Type ← CCTypes.GetRTargetType[indirect.type, cc];
RETURN[[code, type]];
END;
INilRefCCTypesStore: PROC[value: TypedCode, indirect: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
code: Code ← CedarCode.ConcatCode[
indirect.code,
CedarCode.ConcatCode[
value.code,
CedarCode.CodeToStoreUnpopped[indirect.type, value.type]]];
RETURN[[code, value.type]];
END;
INilRefCCTypesLoad: PROC[indirect: TypedCode, cc: CC, procData: REF ANY] RETURNS[TypedCode] =
BEGIN
code: Code ← CedarCode.ConcatCode[
indirect.code,
CedarCode.CodeToLoadThroughIndirect[indirect.type]];
type: Type ← CCTypes.GetRTargetType[indirect.type, cc];
RETURN[[code, type]];
END;
Nodes begin here
There are four types which are candidates for node constructions. Not all are necessary here. Currently (December 15, 1988) PrincOpsFrameContextImpl provides a general mechanism for indirects to fields from which atomic loads and stores will be made. This mechanism suffices for Indirects to Refs. (Eventually, that mechanism will be moved to a more general location that is not target world specific. However, it should still be a general machanism that will cover indirects to Refs.)
That leaves nodes of three varieties: Refs, indirects to RefTargets, and RefTargets.
Ref nodes
RefNodeData: TYPE = RECORD[
indirectToRefTarget: Node,
info: RefTypes.RefNodeInfo];
Note: ClientTarget is frequently refered to as Body in the code in this module.
We can compute the actualRefType safely since the target of a REF can not change its type. Or, another way to say this, is that if type is for a REF ANY, then type is a union type, and the rule is that when loading a union type, we construct a node whose type is the type of the actually loaded value.
CreateRefNode: PUBLIC PROC[type: Type, info: RefTypes.RefNodeInfo, cc: CC] RETURNS[Node] = {
IF NOT CCTypes.IsAnIndirect[CedarCode.GetTypeOfNode[info.indirectToClientTarget], cc] THEN CCE[cirioError, "RefTypesImpl.CreateRefNode given a non-indirect target node"];
{indirectToRefTarget: Node ← CreateIndirectRefTargetNode[info.clientTargetType, info.codeForClientTargetType, info.indirectToClientTarget, cc];
actualRefType: Type ← CreateRefType[cc, NIL--MJS, May 22, 1991: Hypothesis: this is OK 'case it'll never be needed--];
data: REF RefNodeData ← NEW[RefNodeData←[indirectToRefTarget, info]];
SetReferent[actualRefType, info.clientTargetType, info.codeForClientTargetType, cc];
RETURN[CedarCode.CreateCedarNode[RefNodeOps, actualRefType, data]];
}};
CreateNilRefNode: PUBLIC PROC[cc: CC] RETURNS[Node] =
{RETURN[CedarCode.CreateCedarNode[NilRefNodeOps, CreateNilRefType[cc], NIL]]};
RefNodeOps: REF CedarCode.OperationsBody ← NEW[CedarCode.OperationsBody←[
getCurrentType: RefNodeGetCurrentType,
extractField: RefNodeExtractField,
show: RefNodeShow,
getNodeRepresentation: RefNodeGetRepresentation]];
since ref targets never change type, when this node was created it acquired a type that will always be good.
RefNodeGetCurrentType: PROC[node: Node, cc: CC] RETURNS[Type] =
{RETURN[CedarCode.GetTypeOfNode[node]]};
RefNodeExtractField: PROC[id: Rope.ROPE, type: Type, node: Node, cc: CC] RETURNS[Node] =
BEGIN
data: REF RefNodeData ← NARROW[CedarCode.GetDataFromNode[node]];
SELECT TRUE FROM
Rope.Equal[id, "&indirectToBody"] =>
BEGIN
RETURN[CedarCode.SelectFieldFromNode["&body", CedarCode.GetTypeOfNode[data.indirectToRefTarget], data.indirectToRefTarget, cc]];
END;
ENDCASE => CCE[cirioError]; -- shouldn't happen
END;
RefNodeShow: PROC[to: IO.STREAM, node: Node, depth: INT, width: INT, cc: CC] = {
data: REF RefNodeData ← NARROW[CedarCode.GetDataFromNode[node]];
refType: Type ← CedarCode.GetTypeOfNode[node];
refTypeInfo: RefInfo ← NARROW[CCTypes.GetProcDataFromGroundType[refType, cc]];
target: Node ← CedarCode.LoadThroughIndirectNode[CCTypes.GetIndirectType[refTypeInfo.target], data.indirectToRefTarget, cc];
to.PutChar['^];
CedarCode.ShowNode[to, target, depth, width, cc];
RETURN};
RefNodeGetRepresentation: PROC[node: Node, cc: CC] RETURNS[REF ANY] =
BEGIN
data: REF RefNodeData ← NARROW[CedarCode.GetDataFromNode[node]];
RETURN[data.info];
END;
NilRefNodeOps: REF CedarCode.OperationsBody ← NEW[CedarCode.OperationsBody←[
getCurrentType: NilRefNodeGetCurrentType,
extractField: NilRefNodeExtractField,
show: NilRefNodeShow,
getNodeRepresentation: NilRefNodeGetRepresentation]];
NilRefNodeGetCurrentType: PROC[node: Node, cc: CC] RETURNS[Type] =
{CCE[cirioError]};
Not sure what I should implement. A NARROW should always succeed. Not sure if there are other clients.
NilRefNodeExtractField: PROC[id: Rope.ROPE, type: Type, node: Node, cc: CC] RETURNS[Node] =
{CCE[operation, "NIL fault"]};
hmm, if the rope was "&indirectToBody" maybe I should have returned a nilIndirectToClientBody, which would have rejected whatever the subsequent operation was.
NilRefNodeShow: PROC[to: IO.STREAM, node: Node, depth: INT, width: INT, cc: CC]
= {to.PutRope["NIL"]};
NilRefNodeGetRepresentation: PROC[node: Node, cc: CC] RETURNS[REF ANY] =
{RETURN[NIL]};
The interface says this returns NIL.
Indirects to RefTargets
IRefTargetData: TYPE = RECORD[
bodyType: Type,
indirectToBody: Node];
CreateIndirectRefTargetNode: PROC[bodyType: Type, codeForBodyType: INT, indirectToBody: Node, cc: CC] RETURNS[Node] =
BEGIN
irtData: REF IRefTargetData ← NEW[IRefTargetData←[bodyType, indirectToBody]];
refTargetType: Type ← CreateRefTargetType[bodyType, codeForBodyType, cc];
node: Node ← CedarCode.CreateCedarNode[IndirectRTOps, CCTypes.GetIndirectType[refTargetType], irtData];
RETURN[node];
END;
IndirectRTOps: REF CedarCode.OperationsBody ← NEW[CedarCode.OperationsBody←[
getCurrentType: IndirectRTGetCurrentType,
store: IndirectRTStore,
load: IndirectRTLoad,
selectField: IndirectRTSelectField,
show: IndirectRTShow]];
GetCurrentType returns an indirect to the type of the client data in the target, as opposed to the type of the RefTarget, i.e., a pair <type, clientData>. I am not sure what is intended here.
IndirectRTGetCurrentType: PROC[node: Node, cc: CC] RETURNS[Type] =
BEGIN
irtData: REF IRefTargetData ← NARROW[CedarCode.GetDataFromNode[node]];
RETURN[CCTypes.GetIndirectType[irtData.bodyType]];
END;
At this writing, I am now sure when these store will be generated
IndirectRTStore: PROC[valType: Type, valNode: Node, indirectType: Type, indirectNode: Node, cc: CC] =
BEGIN
indirectData: REF IRefTargetData ← NARROW[CedarCode.GetDataFromNode[indirectNode]];
indirectBody: Node ← CedarCode.SelectFieldFromNode["&body", indirectType, indirectNode, cc];
indirectBodyType: Type ← CedarCode.GetTypeOfNode[indirectBody];
valBody: Node ← CedarCode.ExtractFieldFromNode["&body", valType, valNode, cc];
valBodyType: Type ← CedarCode.GetTypeOfNode[valBody];
CedarCode.StoreThroughIndirectNode[valBodyType, valBody, indirectBodyType, indirectBody, cc];
END;
IndirectRTLoad: PROC[indirectType: Type, indirectNode: Node, cc: CC] RETURNS[Node] =
BEGIN -- note: indirectType is compile time, and does not know the actual type.
valType: Type ← CCTypes.GetRTargetType[CedarCode.GetTypeOfNode[indirectNode], cc];
data: REF IRefTargetData ← NARROW[CedarCode.GetDataFromNode[indirectNode]];
RETURN[CreateRTNode[valType, DelayedLoadExtractBody, data, cc, FALSE]];
END;
IndirectRTSelectField: PROC[id: Rope.ROPE, indirectType: Type, indirectNode: Node, cc: CC] RETURNS[Node] =
BEGIN
data: REF IRefTargetData ← NARROW[CedarCode.GetDataFromNode[indirectNode]];
SELECT TRUE FROM
Rope.Equal[id, "&body"] => RETURN[data.indirectToBody];
ENDCASE => CCE[cirioError]; -- shouldn't happen
END;
IndirectRTShow: PROC[to: IO.STREAM, node: Node, depth: INT, width: INT, cc: CC] = {
to.PutChar['^];
IF depth = 0 THEN {to.PutRope["..."]; RETURN};
{
irtType: Type ← CedarCode.GetTypeOfNode[node];
rt: Node ← CedarCode.LoadThroughIndirectNode[irtType, node, cc];
CedarCode.ShowNode[to, rt, depth, width, cc];
RETURN}};
DelayedLoadExtractBody: PROC[procData: REF ANY, cc: CC] RETURNS[Node] =
BEGIN
data: REF IRefTargetData ← NARROW[procData];
indirectBody: Node ← data.indirectToBody;
indirectBodyType: Type ← CedarCode.GetTypeOfNode[indirectBody];
RETURN[CedarCode.LoadThroughIndirectNode[indirectBodyType, indirectBody, cc]];
END;
RefTarget nodes
RTData: TYPE = RECORD[
type: Type,
alreadyIn: BOOLEAN,
extractBody: PROC[procData: REF ANY, cc: CC] RETURNS[Node],
procData: REF ANY];
CreateRTNode: PROC[type: Type, extractBody: PROC[procData: REF ANY, cc: CC] RETURNS[Node], procData: REF ANY, cc: CC, alreadyIn: BOOLEAN] RETURNS[Node] =
BEGIN
rtData: REF RTData ← NEW[RTData←[type, alreadyIn, extractBody, procData]];
node: Node ← CedarCode.CreateCedarNode[RTOps, type, rtData];
RETURN[node];
END;
RTOps: REF CedarCode.OperationsBody ← NEW[CedarCode.OperationsBody←[
forceIn: RTForceIn,
extractField: RTExtractField,
show: RTShow]];
RTForceIn: PROC[type: Type, node: Node, cc: CC] RETURNS[Node] =
BEGIN
rtData: REF RTData ← NARROW[CedarCode.GetDataFromNode[node]];
IF rtData.alreadyIn THEN RETURN[node]
ELSE
BEGIN
nominalBody: Node ← rtData.extractBody[rtData.procData, cc];
bodyType: Type ← CedarCode.GetTypeOfNode[nominalBody];
body: Node ← CedarCode.ForceNodeIn[bodyType, nominalBody, cc];
RETURN[ConstructRTNode[type, body, cc]];
END;
END;
RTExtractField: PROC[id: Rope.ROPE, type: Type, node: Node, cc: CC] RETURNS[Node] =
BEGIN
rtData: REF RTData ← NARROW[CedarCode.GetDataFromNode[node]];
SELECT TRUE FROM
Rope.Equal[id, "&body"] => RETURN[rtData.extractBody[rtData.procData, cc]];
ENDCASE => CCE[cirioError]; -- shouldn't happen
END;
RTShow: PROC[to: IO.STREAM, node: Node, depth: INT, width: INT, cc: CC] = {
rtData: REF RTData ← NARROW[CedarCode.GetDataFromNode[node]];
body: Node ← rtData.extractBody[rtData.procData, cc];
CedarCode.ShowNode[to, body, depth, width, cc]};
Constructed RTNodes
CRTNodeData: TYPE = RECORD[
body: Node];
ConstructRTNode: PROC[type: Type, body: Node, cc: CC] RETURNS[Node] =
BEGIN
data: REF CRTNodeData ← NEW[CRTNodeData←[body]];
RETURN[CreateRTNode[type, CRTNodeExtractBody, data, cc, TRUE]];
END;
CRTNodeExtractBody: PROC[procData: REF ANY, cc: CC] RETURNS[Node] =
BEGIN
data: REF CRTNodeData ← NARROW[procData];
RETURN[NARROW[data.body]];
END;
END..