1
////---------------------------------------------------------------------
2
//// Class definition file: TYPECHECK.CPP
3
//// Associated header file: PARSENODE.H
5
//// Author: Paul C. Gregory
6
//// Creation date: 25/11/99
7
////---------------------------------------------------------------------
10
#include "parsenode.h"
12
START_NAMESPACE( Aqsis )
14
///---------------------------------------------------------------------
15
/// CqParseNodeFunctionCall::TypeCheck
16
/// Do a type check based on suitable types requested, and add a cast if required.
17
/// Uses all possible functions listed to try to fund a suitable candidate.
19
TqInt CqParseNodeFunctionCall::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
21
TqInt NewType = Type_Nil;
22
TqInt cSpecifiedArgs = 0;
24
TqInt* aArgTypes = new TqInt[ m_aFuncRef.size() ];
25
std::vector<CqFuncDef*> aFuncs;
27
CqString strMyName = strName();
29
// Prepare the error string.
30
CqString strErr( strFileName() );
32
strErr += CqString( LineNo() );
36
CqString strErrDesc = "Valid function declaration not found : ";
38
// First discard any function possibilities which don't have the correct number of arguments.
39
// Find out how many arguments have been specified.
40
CqParseNode* pArg = m_pChild;
48
for ( i = 0; i < m_aFuncRef.size(); i++ )
50
aFuncs.push_back( CqFuncDef::GetFunctionPtr( m_aFuncRef[ i ] ) );
51
if ( aFuncs[ i ] != 0 )
53
TqInt cArgs = aFuncs[ i ] ->cTypeSpecLength();
55
( ( !aFuncs[ i ] ->fVarying() && cArgs != cSpecifiedArgs ) ||
56
( aFuncs[ i ] ->fVarying() && cSpecifiedArgs < cArgs ) ) )
58
m_aFuncRef.erase( m_aFuncRef.begin() + i );
59
aFuncs.erase( aFuncs.begin() + i );
65
// If we have eliminated all possibilities, then we have an error.
66
if ( m_aFuncRef.size() == 0 )
68
strErr += "Arguments to function not valid : ";
73
// Then typecheck the arguments.
76
TqBool fVarLen = TqFalse;
79
CqParseNode * pNext = pArg->pNext();
80
// Build an array of possible types.
82
for ( i = 0; i < m_aFuncRef.size(); i++ )
84
if ( aFuncs[ i ] != 0 )
86
if ( aFuncs[ i ] ->cTypeSpecLength() <= iArg || ( aFuncs[ i ] ->fVarying() && aFuncs[ i ] ->cTypeSpecLength() == iArg ) )
88
if ( aFuncs[ i ] ->cTypeSpecLength() >= iArg && aFuncs[ i ] ->fVarying() )
94
aArgTypes[ i ] = aFuncs[ i ] ->aTypeSpec() [ iArg ];
100
// If we have no valid types to check, it must be a variable length function.
103
pArg->TypeCheck( pAllTypes(), Type_Last - 1, TqTrue );
106
// Now type check the argument
107
TqInt ArgType = pArg->TypeCheck( aArgTypes, ctypes, TqTrue );
108
// If no valid function exists for the argument, then this is an error.
109
if ( ArgType == Type_Nil && !fVarLen )
111
strErr += strErrDesc;
115
else if ( ArgType != Type_Nil )
116
pArg->TypeCheck( &ArgType, 1 ); // Now do a real typecast
118
// Check the list of possibles, and remove any that don't take the returned argument at the current point in the
120
for ( i = 0; i < m_aFuncRef.size(); i++ )
122
if ( strlen( aFuncs[ i ] ->strParams() ) <= iArg || aFuncs[ i ] ->strParams() [ iArg ] == '*' )
125
TqInt type = aFuncs[ i ] ->aTypeSpec() [ iArg ];
126
if ( ( type & Type_Mask ) != ( ArgType & Type_Mask ) )
128
m_aFuncRef.erase( m_aFuncRef.begin() + i );
129
aFuncs.erase( aFuncs.begin() + i );
134
// If we have an upper case type in the function declaration, then we need an actual variable.
135
if ( type & Type_Variable && !pArg->IsVariableRef() )
137
strErrDesc = "Argument ";
138
strErrDesc += static_cast<TqInt>( iArg );
139
strErrDesc += " must be a variable";
140
m_aFuncRef.erase( m_aFuncRef.begin() + i );
141
aFuncs.erase( aFuncs.begin() + i );
151
// Check if any of the remaining functions return the correct type.
152
for ( i = 0; i < m_aFuncRef.size(); i++ )
154
if ( aFuncs[ i ] != 0 )
157
for ( j = 0; j < Count; j++ )
159
if ( aFuncs[ i ] ->Type() == pTypes[ j ] )
161
// Set the selected function declaration as top.
162
m_aFuncRef[ 0 ] = m_aFuncRef[ i ];
163
return ( pTypes[ j ] );
169
// If we got here, we must cast the return value to a suitable type.
170
for ( i = 0; i < m_aFuncRef.size(); i++ )
172
if ( aFuncs[ i ] != 0 )
175
for ( j = 0; j < Count; j++ )
177
if ( ( NewType = FindCast( aFuncs[ i ] ->Type(), pTypes, Count ) ) != Type_Nil )
179
// Set the selected function declaration as top.
180
m_aFuncRef[ 0 ] = m_aFuncRef[ i ];
181
// Add a cast to the new type.
182
CqParseNode* pCast = new CqParseNodeCast( NewType );
190
strErr += strErrDesc;
196
///---------------------------------------------------------------------
197
/// CqParseNodeFunctionCall::CheckArgCast
198
/// Check if it is possible to convert the arguments passed to this function
199
/// into valid arguments for any of its function declarations.
201
void CqParseNodeFunctionCall::CheckArgCast( std::vector<TqInt>& aRes )
203
// Find out how many arguments have been specified.
205
std::vector<TqInt> aArgTypes;
206
CqParseNode* pArg = m_pChild;
210
aArgTypes.push_back( pArg->ResType() );
211
pArg = pArg->pNext();
214
// For each posible declaration...
216
for ( i = 0; i < aRes.size(); i++ )
218
CqFuncDef* pFuncDef = CqFuncDef::GetFunctionPtr( m_aFuncRef[ aRes[ i ] ] );
219
if ( pFuncDef == 0 ) continue;
221
// ...build a list of variable types from its parameter type string.
222
std::vector<TqInt>& aTypes = pFuncDef->aTypeSpec();
224
// Check the number of arguments first.
225
if ( ( aTypes.size() != cArgs && !pFuncDef->fVarying() ) ||
226
aTypes.size() > cArgs )
228
aRes.erase( aRes.begin() + i );
233
// Now check through to see if the specified arguments can be converted to the required ones.
234
TqBool fValid = TqTrue;
236
for ( j = 0; j < aTypes.size(); j++ )
237
if ( FindCast( aArgTypes[ j ], &aTypes[ j ], 1 ) == Type_Nil ) fValid = TqFalse;
239
// If we have found a match, return it.
242
aRes.erase( aRes.begin() + i );
249
///---------------------------------------------------------------------
250
/// CqParseNodeFunctionCall::ArgCast
251
/// Convert the arguments passed to this function into valid arguments for the specified function declaration.
253
void CqParseNodeFunctionCall::ArgCast( TqInt iIndex )
255
// NOTE: It is presumed that CheckArgCast has been called prior to this to
256
// ensure that the argument list can validly be cast the the required parameters.
258
CqFuncDef * pFuncDef = CqFuncDef::GetFunctionPtr( m_aFuncRef[ iIndex ] );
259
if ( pFuncDef == 0 ) return ;
261
// Build a list of variable types from its parameter type string.
262
std::vector<TqInt>& aTypes = pFuncDef->aTypeSpec();
264
// Now convert the arguments to the correct types
265
CqParseNode* pArg = m_pChild;
267
while ( pArg != 0 && j < aTypes.size() )
269
// Must get the next here incase the check inserts a cast.
270
CqParseNode * pNext = pArg->pNext();
271
pArg->TypeCheck( &aTypes[ j++ ] );
277
///---------------------------------------------------------------------
278
/// CqParseNodeUnresolvedCall::TypeCheck
279
/// This is rather awkward, we will convert to whichever type we are asked
280
/// to be, then later when the shader vm parses the .slx we can try and build
281
/// a cast then, the same goes for the arguments.
283
TqInt CqParseNodeUnresolvedCall::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
285
TqInt NewType = Type_Nil;
287
// TypeCheck the args, we don't know what types will be needed
288
// later, so there is little else we can do.
289
CqParseNode *pArg = m_pChild;
292
CqParseNode *pNext = pArg->pNext();
293
pArg->TypeCheck( pAllTypes(), Type_Last - 1 );
297
// If we have no type set yet we take the first type given as an option
298
if(m_aFuncDef.Type() == Type_Nil || !CheckOnly)
300
// Is Void acceptable, if so we prefer it.
302
for( i=0; i < Count; i++)
304
if( pTypes[i] == Type_Void)
307
// Otherwise we fall back to the first option given
308
if( NewType == Type_Nil )
311
m_aFuncDef = CqFuncDef( NewType,
312
m_aFuncDef.strName(),
314
m_aFuncDef.strParams(),
315
m_aFuncDef.pDefNode(),
319
return m_aFuncDef.Type();
323
///---------------------------------------------------------------------
324
/// CqParseNodeVariable::TypeCheck
325
/// Do a type check based on suitable types requested, and add a cast in required.
327
TqInt CqParseNodeVariable::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
329
// Check if the variable type matches any of the requested ones.
330
TqInt MyType = ( ResType() & Type_Mask );
332
for ( i = 0; i < Count; i++ )
334
if ( pTypes[ i ] == MyType )
337
// If we got here, we are not of the correct type, so find a suitable cast.
338
TqInt NewType = FindCast( MyType, pTypes, Count );
339
CqParseNodeCast* pCast = new CqParseNodeCast( NewType );
342
if ( NewType == Type_Nil && !CheckOnly )
344
CqString strErr( strFileName() );
348
strErr += "Cannot convert from type ";
349
strErr += CqParseNode::TypeName( MyType );
350
strErr += " to any of the required types";
358
///---------------------------------------------------------------------
359
/// CqParseNodeVariableArray::TypeCheck
360
/// Do a type check based on suitable types requested, and add a cast in required.
362
TqInt CqParseNodeVariableArray::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
364
// Check if the child can be made into a float.
365
TqInt aType = Type_Float;
366
if ( m_pChild->TypeCheck( &aType, 1, CheckOnly ) == Type_Nil )
368
TqInt IndexType = ( m_pChild->ResType() & Type_Mask );
369
CqString strErr( strFileName() );
373
strErr += "Array index must be float type : ";
374
strErr += CqParseNode::TypeName( IndexType );
379
return ( CqParseNodeVariable::TypeCheck( pTypes, Count, CheckOnly ) );
383
///---------------------------------------------------------------------
384
/// CqParseNodeAssign::TypeCheck
385
/// Do a type check based on suitable types requested, and add a cast in required.
387
extern EqShaderType gShaderType;
389
TqInt CqParseNodeAssign::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
391
// First check if the assign is to a read only variable.
392
if( CqVarDef::GetVariablePtr( m_VarRef ) &&
394
CqVarDef::GetVariablePtr( m_VarRef )->ReadOnly( pShaderNode()->ShaderType() ) )
396
CqString strErr( strFileName() );
400
strErr += "Cannot access read only variable '";
401
strErr += CqVarDef::GetVariablePtr( m_VarRef )->strName();
402
strErr += "' in shader type '";
403
strErr += gShaderTypeNames[ pShaderNode()->ShaderType() ];
409
TqInt MyType = ( ResType() & Type_Mask );
410
// Type check the assignment expression first.
411
CqParseNode* pExpr = m_pChild;
412
if ( pExpr->TypeCheck( &MyType, 1, CheckOnly ) != MyType )
413
return ( Type_Nil ); // TODO: Should throw an exception here.
415
// Check if the variable type matches any of the requested ones.
417
for ( i = 0; i < Count; i++ )
419
if ( pTypes[ i ] == MyType )
422
// If we got here, we are not of the correct type, so find a suitable cast.
423
TqInt NewType = FindCast( MyType, pTypes, Count );
424
CqParseNodeCast* pCast = new CqParseNodeCast( NewType );
427
if ( NewType == Type_Nil && !CheckOnly )
429
CqString strErr( strFileName() );
433
strErr += "Cannot convert from type ";
434
strErr += CqParseNode::TypeName( MyType );
435
strErr += " to any of the required types";
443
///---------------------------------------------------------------------
444
/// CqParseNodeAssignArray::TypeCheck
445
/// Do a type check based on suitable types requested, and add a cast in required.
447
TqInt CqParseNodeAssignArray::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
449
// Check if the child can be made into a float.
450
TqInt aType = Type_Float;
451
if ( m_pChild->pNext() ->TypeCheck( &aType, 1, CheckOnly ) == Type_Nil )
453
TqInt IndexType = ( m_pChild->pNext() ->ResType() & Type_Mask );
454
CqString strErr( strFileName() );
458
strErr += "Array index must be float type : ";
459
strErr += CqParseNode::TypeName( IndexType );
464
return ( CqParseNodeAssign::TypeCheck( pTypes, Count, CheckOnly ) );
468
///---------------------------------------------------------------------
469
/// CqParseNodeOp::TypeCheck
470
/// Do a type check based on suitable types requested, and add a cast in required.
472
TqInt CqParseNodeOp::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
474
// Check if both arguments to the operator match type.
475
CqParseNode * pOperandA = m_pChild;
476
CqParseNode* pOperandB = m_pChild->pNext();
477
assert( pOperandA != 0 && pOperandB != 0 );
478
TqInt TypeA = ( pOperandA->ResType() & Type_Mask );
479
TqInt TypeB = ( pOperandB->ResType() & Type_Mask );
481
// See if they can both be cast to a requested type.
483
for ( i = 0; i < Count; i++ )
485
if ( FindCast( TypeA, &pTypes[ i ], 1 ) != Type_Nil )
487
if ( FindCast( TypeB, &pTypes[ i ], 1 ) != Type_Nil )
489
// Add any cast operators required.
490
pOperandA->TypeCheck( &pTypes[ i ], 1, CheckOnly );
491
pOperandB->TypeCheck( &pTypes[ i ], 1, CheckOnly );
492
return ( pTypes[ i ] );
499
CqString strErr( strFileName() );
503
strErr += "Cannot find a suitable cast for the operands.";
512
///---------------------------------------------------------------------
513
/// CqParseNodeRelOp::TypeCheck
514
/// Do a type check based on suitable types requested, and add a cast in required.
516
TqInt CqParseNodeRelOp::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
518
// Type check the operands first.
519
CqParseNode * pExpr1 = m_pChild;
520
CqParseNode* pExpr2 = pExpr1->pNext();
521
pExpr1->TypeCheck( pAllTypes(), Type_Last - 1, CheckOnly );
522
pExpr2->TypeCheck( pAllTypes(), Type_Last - 1, CheckOnly );
524
// See if float is a requested type.
526
if ( ( NewType = FindCast( Type_Float, pTypes, Count ) ) != Type_Nil )
528
if ( NewType == Type_Float ) return ( Type_Float );
531
CqParseNodeCast* pCast = new CqParseNodeCast( NewType );
539
CqString strErr( strFileName() );
543
strErr += "Relative operators only return float.";
552
///---------------------------------------------------------------------
553
/// CqParseNodeUnaryOp::TypeCheck
554
/// Do a type check based on suitable types requested, and add a cast in required.
556
TqInt CqParseNodeUnaryOp::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
558
// Check if the operand can be cast to a requested type.
560
return ( m_pChild->TypeCheck( pTypes, Count, CheckOnly ) );
566
///---------------------------------------------------------------------
567
/// CqParseNodeOpDot::TypeCheck
568
/// Do a type check based on suitable types requested, and add a cast in required.
570
TqInt CqParseNodeMathOpDot::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
572
static TqInt aArgTypes[ 3 ] = {Type_Point, Type_Vector, Type_Normal};
574
// Get the argument types.
575
CqParseNode* pOperandA = m_pChild;
576
CqParseNode* pOperandB = m_pChild->pNext();
577
assert( pOperandA != 0 && pOperandB != 0 );
578
TqInt TypeA = ( pOperandA->ResType() & Type_Mask );
579
TqInt TypeB = ( pOperandB->ResType() & Type_Mask );
581
// Dot operator can only take normal/vector/point types, check the
582
// arguments can be made to match this, and always returns a float,
583
// check this is valid.
586
if ( ( RetType = FindCast( Type_Float, pTypes, Count ) ) != Type_Nil )
588
TqBool fValid = TqFalse;
590
for ( i = 0; i < sizeof( aArgTypes ) / sizeof( aArgTypes[ 0 ] ); i++ )
592
if ( FindCast( TypeA, &aArgTypes[ i ], 1 ) != Type_Nil )
594
if ( FindCast( TypeB, &aArgTypes[ i ], 1 ) != Type_Nil )
596
// Add any cast operators required.
597
pOperandA->TypeCheck( &aArgTypes[ i ], 1, CheckOnly );
598
pOperandB->TypeCheck( &aArgTypes[ i ], 1, CheckOnly );
606
if ( RetType != Type_Float )
608
CqParseNodeCast * pCast = new CqParseNodeCast( RetType );
617
CqString strErr( strFileName() );
621
strErr += "Cannot find a suitable cast for the operands.";
630
///---------------------------------------------------------------------
631
/// CqParseNodeConst::TypeCheck
632
/// Perform a typecheck, and add cast if required.
634
TqInt CqParseNodeConst::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
636
// Check if the variable type matches any of the requested ones.
637
TqInt MyType = ResType();
639
for ( i = 0; i < Count; i++ )
641
if ( pTypes[ i ] == MyType )
645
TqInt NewType = FindCast( MyType, pTypes, Count );
646
// If we got here, we are not of the correct type, so find a suitable cast.
649
CqParseNodeCast * pCast = new CqParseNodeCast( NewType );
653
if ( NewType == Type_Nil && !CheckOnly )
655
CqString strErr( strFileName() );
659
strErr += "Cannot convert from type ";
660
strErr += CqParseNode::TypeName( MyType );
661
strErr += " to any of the required types";
669
///---------------------------------------------------------------------
670
/// CqParseNodeCast::TypeCheck
671
/// Do a type check based on suitable types requested, and add a cast in required.
673
TqInt CqParseNodeCast::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
675
// Perform a typecheck on the cast expression
676
CqParseNode * pExpr = m_pChild;
677
pExpr->TypeCheck( &m_tTo, 1, CheckOnly );
679
// Check the return type, and add another cast if necessary.
680
// Note: We add another cast to allow for forced casts of function return types,
681
// i.e. we want the sl code "Ci=float noise(P)"
682
// to call float noise THEN cast to a color, not call color noise.
684
for ( i = 0; i < Count; i++ )
685
if ( pTypes[ i ] == m_tTo ) return ( m_tTo );
687
TqInt NewType = FindCast( m_tTo, pTypes, Count );
688
if ( NewType == Type_Nil && !CheckOnly )
690
CqString strErr( strFileName() );
694
strErr += "Cannot convert from type ";
695
strErr += CqParseNode::TypeName( NewType );
696
strErr += " to any of the required types";
704
CqParseNodeCast * pCast = new CqParseNodeCast( NewType );
713
///---------------------------------------------------------------------
714
/// CqParseNodeTriple::TypeCheck
715
/// Do a type check based on suitable types requested, and add a cast in required.
717
TqInt CqParseNodeTriple::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
719
static TqInt ExprType = Type_Float;
720
// Perform a typecheck on the three float expressions
721
CqParseNode* pExpr = m_pChild;
724
pExpr->TypeCheck( &ExprType, 1, CheckOnly );
725
pExpr = pExpr->pNext();
727
// Check if expecting a triple, if not add a cast
729
for ( i = 0; i < Count; i++ )
730
if ( pTypes[ i ] == Type_Triple ) return ( Type_Triple );
732
// If we got here, we are not of the correct type, so find a suitable cast.
733
TqInt NewType = FindCast( Type_Triple, pTypes, Count );
734
CqParseNodeCast* pCast = new CqParseNodeCast( NewType );
737
if ( NewType == Type_Nil && !CheckOnly )
739
CqString strErr( strFileName() );
743
strErr += "Cannot convert from type ";
744
strErr += CqParseNode::TypeName( NewType );
745
strErr += " to any of the required types";
753
///---------------------------------------------------------------------
754
/// CqParseNodeHexTuple::TypeCheck
755
/// Do a type check based on suitable types requested, and add a cast in required.
757
TqInt CqParseNodeHexTuple::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
759
static TqInt ExprType = Type_Float;
760
// Perform a typecheck on the three float expressions
761
CqParseNode* pExpr = m_pChild;
764
pExpr->TypeCheck( &ExprType, 1, CheckOnly );
765
pExpr = pExpr->pNext();
767
// Check if expecting a sixteentuple, if not add a cast
769
for ( i = 0; i < Count; i++ )
770
if ( pTypes[ i ] == Type_HexTuple ) return ( Type_HexTuple );
772
// If we got here, we are not of the correct type, so find a suitable cast.
773
TqInt NewType = FindCast( Type_Matrix, pTypes, Count );
774
CqParseNodeCast* pCast = new CqParseNodeCast( NewType );
777
if ( NewType == Type_Nil && !CheckOnly )
779
CqString strErr( strFileName() );
783
strErr += "Cannot convert from type ";
784
strErr += CqParseNode::TypeName( NewType );
785
strErr += " to any of the required types";
793
///---------------------------------------------------------------------
794
/// CqParseNodeCommFunction::TypeCheck
796
TqInt CqParseNodeCommFunction::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
798
// Check if the variable type matches any of the requested ones.
799
TqInt MyType = ResType();
801
for ( i = 0; i < Count; i++ )
803
if ( pTypes[ i ] == MyType )
806
// If we got here, we are not of the correct type, so find a suitable cast.
807
TqInt NewType = FindCast( MyType, pTypes, Count );
808
CqParseNodeCast* pCast = new CqParseNodeCast( NewType );
811
if ( NewType == Type_Nil && !CheckOnly )
813
CqString strErr( strFileName() );
817
strErr += "Cannot convert from type ";
818
strErr += CqParseNode::TypeName( MyType );
819
strErr += " to any of the required types";
827
///---------------------------------------------------------------------
828
/// CqParseNodeQCond::TypeCheck
829
/// Type check the return from a conditional statement and cast the true/false expressions if necessary.
831
TqInt CqParseNodeQCond::TypeCheck( TqInt* pTypes, TqInt Count, TqBool CheckOnly )
833
// Ensure that the conditional expression is type checked.
834
CqParseNode * pCondExp = m_pChild;
835
assert( pCondExp != 0 );
836
pCondExp->TypeCheck( pAllTypes(), Type_Last - 1, CheckOnly );
838
// Check if both expressions match type.
839
CqParseNode * pTrue = m_pChild->pNext();
840
assert( pTrue != 0 );
841
CqParseNode* pFalse = pTrue->pNext();
842
assert( pFalse != 0 );
844
TqInt TypeT = ( pTrue->ResType() & Type_Mask );
845
TqInt TypeF = ( pFalse->ResType() & Type_Mask );
847
// See if they can both be cast to a requested type.
849
for ( i = 0; i < Count; i++ )
851
if ( FindCast( TypeT, &pTypes[ i ], 1 ) != Type_Nil )
853
if ( FindCast( TypeF, &pTypes[ i ], 1 ) != Type_Nil )
855
// Add any cast operators required.
856
pTrue->TypeCheck( &pTypes[ i ], 1, CheckOnly );
857
pFalse->TypeCheck( &pTypes[ i ], 1, CheckOnly );
858
return ( pTypes[ i ] );
865
CqString strErr( strFileName() );
869
strErr += "Cannot find a suitable cast for the expressions.";
878
END_NAMESPACE( Aqsis )
879
//---------------------------------------------------------------------