Wiki

root/cobra/trunk/Source/Members.cobra

Revision 2636, 57.4 KB (checked in by Charles.Esterbrook, 7 weeks ago)

JVM back-end progress.
credit:hopscc

  • Property svn:eol-style set to native
Line 
1"""
2This module contains BoxMember on down, except Property and Indexer which have their own files.
3"""
4
5interface IBoxMember inherits IMember is partial
6    """
7    Anything that can be a member of a box should implement IBoxMember.
8    This includes methods, properties, enums and more.
9    It even includes Box as nested boxes are an anticipated feature.
10    """
11    get docString as String?
12    get idToken as IToken
13    get isNames as String*
14    get parentBox as Box
15    get token as IToken
16
17    def cobraSourceSignature as String
18    def mangleName(name as String) as String
19        # TODO require .compiler.curBoxMember is this
20    def mergedIntoPartialBox(newBox as Box)
21        require
22            newBox is not .parentBox
23            newBox.name == .parentBox.name
24
25
26interface IBoxCodeMember inherits IBoxMember
27
28    get hasParams as bool
29    get isExtensionMember as bool
30    get params as IList<of Param>
31    get testMethods as IList<of TestMethod>
32
33    def matchesSignature(member as IBoxCodeMember) as bool
34   
35
36interface IOverloadable inherits IBoxCodeMember
37    """
38    Implemented by AbstractMethod and Indexer.
39    """
40
41    pro overloadGroup as MemberOverload?
42    def cobraSourceSignature(includeShared as bool) as String
43
44
45class BoxMember
46    is abstract, partial
47    inherits NamedNode
48    implements IBoxMember
49
50    shared
51        var _accessLevels = ['public', 'protected', 'internal', 'protected internal', 'private']
52            """
53            The access level names are the same in both Cobra and C#.
54            """
55
56    var _idToken as IToken
57    var _parentBox as Box
58    var _docString as String?
59    var _isNames as IList<of String>?
60    var _attribs as AttributeList
61    var _overloadGroup as MemberOverload?
62    var _testMethods as List<of TestMethod>?
63    var _matchingBaseMember as IBoxCodeMember?
64    var _sharedMethodBacking as String?
65    var _sharedMethodBackingIsAlias as bool
66    var _aliasedMethodBacking as String?
67    var _binaryName as String?
68
69    cue init(token as IToken, idToken as IToken, parentBox as Box, name as String, isNames as String*, attribs as AttributeList)
70        .init(token, idToken, parentBox, name, isNames, attribs, nil)
71
72    cue init(token as IToken, idToken as IToken, parentBox as Box, name as String, isNames as String*, attribs as AttributeList, docString as String?)
73        base.init(idToken, name)
74        _idToken = idToken
75        _isNames = isNames.toList
76        _attribs = attribs
77        _parentBox = parentBox
78        _docString = docString
79
80    ## Properties
81
82    get attributes from _attribs
83
84    get idToken from var
85   
86    get parentBox from var
87
88    get canHaveMatchingBaseMember as bool
89        """
90        Used by _computeMatchingBaseMembers to skip over members that cannot have base members.
91        Typically, explicit interface implementations like 'ICollection.IsSynchronized'
92        """
93        return '.' not in _name
94
95    pro binaryName from var
96        """
97        If this BoxMember was scanned from an assembly (usually a DLL), this property holds the assembly-based name.
98        For example, you might say "writeLine" in Cobra, but the binary name is probably "WriteLine" and *could be* "writeLine".
99        """
100
101    pro docString from var
102
103    get isNames as String*
104        for name in _isNames, yield name
105
106    pro overloadGroup from var
107
108    pro sharedMethodBacking from var
109
110    pro sharedMethodBackingIsAlias from var
111
112    pro aliasedMethodBacking from var
113   
114    get testMethods as IList<of TestMethod>
115        if _testMethods is nil, _testMethods = List<of TestMethod>()
116        return _testMethods to !
117
118    get isAbstract as bool
119        return 'abstract' in _isNames
120
121    get isCallable as bool
122        return false
123
124    get isInternal as bool
125        return 'internal' in _isNames
126
127    get isOverride as bool
128        return 'override' in _isNames
129
130    get isProtected as bool
131        return 'protected' in _isNames
132
133    get isPrivate as bool
134        return 'private' in _isNames
135
136    get isPublic as bool
137        return 'public' in _isNames
138
139    get isReadOnly as bool
140        return 'readonly' in _isNames
141       
142    get isShared as bool
143        return 'shared' in _isNames
144
145    pro isUsed as bool
146        get
147            return base.isUsed
148        set
149            base.isUsed = value
150            .resultType.isUsed = true
151
152    get englishName as String is abstract
153
154    get defaultAccessLevel as String
155        if .isInterfaceMember and not .isExtensionMember
156            return ''
157        if .name.startsWith('__')
158            return 'private'
159        if .name.startsWith('_')
160            return if(.isStructMember, 'private', 'protected')
161        return 'public'
162
163    get hasVariParams as bool
164        """
165        Retuns true if the class member takes variable number of arguments.
166        """
167        return false
168
169    get hasParams as bool
170        """
171        Returns true if the class member has one or more parameters defined.
172        """
173        return .params.count > 0
174
175    get params as IList<of Param>
176        """
177        Returns the list of params of this method, possibly empty.
178        Checking hasParams first is recommended since it's more efficient.
179        """
180        return List<of Param>()
181
182    get isClassMember as bool
183        return _parentBox inherits Class or (_parentBox inherits Extension and (_parentBox to Extension).extendedBox inherits Class)
184
185    get isInterfaceMember as bool
186        return _parentBox inherits Interface or (_parentBox inherits Extension and (_parentBox to Extension).extendedBox inherits Interface)
187
188    get isStructMember as bool
189        return _parentBox inherits Struct or (_parentBox inherits Extension and (_parentBox to Extension).extendedBox inherits Struct)
190
191    get isExtensionMember as bool
192        return _parentBox inherits Extension
193
194    pro matchingBaseMember as IBoxCodeMember?
195        get
196            return _matchingBaseMember
197        set
198            _matchingBaseMember = value
199
200    pro parentNameSpace as NameSpace?
201        get
202            return nil
203        set
204            throw NotSupportedException()
205
206    get requiresThis as bool
207        return not _name.startsWith('_')
208
209    get resultType as IType is abstract
210        """
211        The result type of the var, method or property.
212        """
213
214    get shouldBeVirtual as bool
215        return false
216
217
218    ## Other
219
220    def cobraSourceSignature as String
221        return .idString
222
223    def unNilReturnType
224        """
225        Subclasses should override to change their "return type node" to non-nilable.
226        No need to invoke `base`.
227        This is invoked to fix up the CLR library which does not indicate, for example, that StringBuilder.toString returns String not String?
228        """
229        pass
230
231    def mangleName(name as String) as String
232        require .compiler.curBoxMember is this
233        return name
234       
235    def mergedIntoPartialBox(newBox as Box)
236        require
237            newBox is not .parentBox
238            newBox.name == .parentBox.name
239        body
240            _parentBox = newBox
241
242    def removeIsName(name as String)
243        require name in .isNames
244        _isNames.remove(name)
245
246
247    ## INamedNode
248
249    get typeForIdentifier as IType is override
250        return .resultType
251
252    get typeForReceiver as IType is override
253        return .resultType
254
255
256    ## Binding
257
258    def _bindInt is override
259        base._bindInt
260        isNames = Set<of String>()
261        for name in _isNames
262            if name in isNames and not .parentBox.isFromBinaryLibrary
263                .compiler.warning(this, 'Duplicate modifier "[name]". You can remove it.')
264            isNames.add(name)
265        _isNames = List<of String>(isNames)
266        if .defaultAccessLevel.length
267            # CC: if not find level in _accessLevel where level in _isNames
268            found = false
269            for level in _accessLevels
270                if level in _isNames
271                    found = true
272                    break
273            if not found
274                _isNames.insert(0, .defaultAccessLevel)
275        if .shouldBeVirtual
276            found = false
277            for name in ['nonvirtual', 'virtual', 'shared', 'override', 'abstract']
278                if name in _isNames
279                    found = true
280                    break
281            if not found
282                _isNames.add('virtual')
283        for attrib in _attribs
284            attrib.bindInt
285        if _testMethods and _testMethods.count
286            for tm in _testMethods, tm.bindInt
287
288    def bindImp as dynamic
289        assert not .parentBox.isConstructed
290        return base.bindImp
291           
292    def _bindImp is override
293        base._bindImp
294        assert .didBindInt  # class members should have always received a bindInt first
295        assert not .parentBox.isConstructed  # not expecting to bindImp on a constructed type
296        for attrib in _attribs, attrib.bindImp
297        if _testMethods and _testMethods.count
298            for tm in _testMethods, tm.bindImp
299
300    def usesBase
301        # Needed by AbstractMethod and ProperDexer
302        if 'new' in _isNames or 'override' in _isNames
303            return
304        # using base implies override, but only if the method sig in the base class is the same
305        # (otherwise it's just an overload)
306        if (parentBox = .parentBox) inherits Class
307            assert parentBox.baseClass
308            # Why not use .matchingBaseMember? Because currently that's only method-to-method. Does not work for properties.
309            baseMember = parentBox.baseClass.memberForName(_name)
310            if baseMember
311                if baseMember inherits MemberOverload
312                    for member in baseMember.members
313                        if .matchesSignature(member)
314                            baseMember = member
315                            break
316                if baseMember inherits IBoxCodeMember
317                    if .matchesSignature(baseMember)
318                        if 'nonvirtual' in (baseMember).isNames
319                            _isNames.add('new')
320                        else
321                            _isNames.add('override')
322                        _isNames.remove('virtual'# virtual is the default for properties and methods
323                else
324                    throw FallThroughException(baseMember)
325
326    def matchesSignature(member as IBoxCodeMember) as bool
327        if .name <> member.name, return false
328        params, otherParams = .params, member.params
329        if params.count <> otherParams.count, return false
330        for i in params.count
331            if not _matchesForOverride(params[i].type, otherParams[i].type)
332                return false
333        return true
334
335    def _matchesForOverride(a as IType, b as IType) as bool
336        if a == b
337            return true
338        if (a inherits NilableType) <> (b inherits NilableType)
339            return a.nonNil == b.nonNil
340        return false
341
342
343    ## Fields
344
345    def addMinFields is override
346        base.addMinFields
347        .addField('name', _name)
348        .addField('isNames', _isNames)
349
350    def addRefFields is override
351        base.addRefFields
352        .addField('parentBox', .parentBox)
353
354    def addSubFields is override
355        base.addSubFields
356        if _isNames.count
357            .addField('isNames', _isNames)
358        .addField('docString', _docString)
359
360
361    ## Generics
362   
363    def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember  # CC: as same
364        require .didBindInt
365        newMember = .memberwiseClone to BoxMember
366        newMember._parentBox = box
367        newMember._overloadGroup = nil
368        return newMember
369
370
371class BoxEvent
372    is partial
373    inherits BoxMember
374   
375    var _handlerType as IType?
376    var _handlerTypeProxy as ITypeProxy?
377
378    cue init(token as IToken, idToken as IToken, box as Box, name as String, isNames as String*, attribs as AttributeList, docString as String?, handlerType as ITypeProxy)
379        base.init(token, idToken, box, name, isNames, attribs, docString)
380        _handlerTypeProxy = handlerType
381
382    get englishName as String is override
383        return 'event'
384
385    get handlerType from var
386        """
387        Returns the underlying handler type for the event.
388        """
389   
390    get handlerTypeProxy from var
391
392    get isCallable as bool is override
393        return true
394
395    get params as IList<of Param> is override
396        assert .didBindInt
397        if _handlerType inherits MethodSig
398            return _handlerType.params
399        else if _handlerType inherits Class
400            method = _handlerType.memberForName('invoke')
401            if method inherits AbstractMethod
402                return method.params
403            else
404                .compiler.warning(this, 'Cannot locate a single invoke method of "[_handlerType.name]".')
405                return List<of Param>()         
406        else
407            if not .hasError
408                .compiler.warning(this, 'Cannot determine parameters of event "[.name]".')
409            return List<of Param>()         
410
411    get resultType as IType is override
412        return .handlerType to !
413
414    def addSubFields
415        base.addSubFields
416        .addField('handlerType', _handlerType)
417        .addField('handlerTypeProxy', _handlerTypeProxy)
418
419    def _bindInt
420        base._bindInt
421        if not _handlerType
422            _handlerType = _handlerTypeProxy.realType
423        # TODO: error check that _handlerType is a delegate
424        # _handlerType.nonNil.isDescendantOf(.compiler.delegateType)
425
426
427class BoxField inherits BoxMember is abstract, partial
428    """
429    The abstract base class for BoxConst and BoxVar.
430    """
431   
432    var _typeNode as ITypeProxy?
433    var _type as IType?
434    var _initExpr as Expr?
435    var _isAssignedTo as bool
436
437    cue init(token as IToken, idToken as IToken, box as Box, name as String, typeNode as ITypeProxy?, isNames as String*, initExpr as Expr?, attribs as AttributeList?, docString as String)
438        base.init(token, idToken, box, name, isNames, attribs ? AttributeList(), docString)
439        _typeNode = typeNode
440        _initExpr = initExpr
441
442    def addRefFields is override
443        base.addRefFields
444        if _type, .addField('type', _type)
445        else, .addField('typeNode', _typeNode)
446
447    def addSubFields is override
448        base.addSubFields
449        .addField('initExpr', _initExpr)
450
451    pro isAssignedTo from var
452
453    get requiresDotPrefix as bool
454        return not _name.startsWith('_')
455       
456    get resultType as IType is override
457        """
458        The result type of the var, method or property.
459        """
460        t = .type
461        if t, return t
462        else, throw FallThroughException('_type is nil. this=[this]')
463
464    pro type as IType?
465        get
466            return _type
467        set
468            assert false, 'Cannot set the type of a box field.'
469
470    get initExpr from var       
471
472    def _bindInt is override
473        base._bindInt
474        assert _typeNode or _initExpr
475        if _type is nil and _typeNode
476            _type = _typeNode.realType
477        if _initExpr
478            _initExpr.bindImp  # that's bindImp intentionally
479            if _type is nil
480                _type = _initExpr.type
481            else if not _initExpr.canBeAssignedTo(_type to !)
482                .throwError('Incompatible types. Cannot assign value of type [_initExpr.type.name] on the right to [_type.name] on the left.')
483        _checkForBaseDuplicate
484   
485    def _checkForBaseDuplicate
486        if not .parentBox.isFromBinaryLibrary and .parentBox.baseClass and .parentBox.baseClass.memberForName(.name)
487            ancestor = (.parentBox.baseClass.memberForName(.name) to IBoxMember).parentBox
488            .recordError('Cannot declare a [.englishName] named "[.name]" because an inherited member with that name already exists in "[ancestor.name]".')
489
490    def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember  # CC: as same
491        assert .type
492        newMember = base.constructedFor(box, gpToType) to BoxField
493        assert newMember._type
494        newMember._type = newMember._type.secondaryConstructedTypeFor(box, gpToType)
495        return newMember
496
497
498class BoxConst
499    is partial
500    inherits BoxField
501    """
502    A constant such as:
503        const PI = 3.1415926535897931
504   
505    In practice, when developing libraries, a read-only var is more practical than a constant,
506    because if you update/correct its value, the programs that depend on it will see the update.
507    Constants, on the other hand, are inlined in code and can only be updated in programs by
508    recompiling against a newer library.
509   
510    Also, in practice, properties may be more practical than constants or read-only variables.
511    Their implementation can be revamped later to retrieve values from a config file, database,
512    test harness, etc.
513    """     
514
515    cue init(token as IToken, idToken as IToken, box as Box, name as String, typeNode as ITypeProxy?, isNames as String*, initExpr as Expr?, attribs as AttributeList?, docString as String)
516        base.init(token, idToken, box, name, typeNode, isNames, initExpr, attribs, docString)
517
518    get isShared as bool is override
519        return true
520
521    get englishName as String is override
522        return 'constant member'
523
524    def _bindInt is override
525        base._bindInt
526        if not _initExpr and not .parentBox.isFromBinaryLibrary
527            .throwError('A constant must be explicitly initialized on declaration')
528
529
530class BoxVar
531    is partial
532    inherits BoxField
533    implements IVar
534    """
535    A BoxVar is a variable declared for a box, whether instance or shared. For example:
536        var _x as int
537        var _y as int
538        var _name = ''
539        var _nextSerialNum as int is shared
540
541    Classes and structs can have vars, but interfaces cannot.
542    """
543
544    var _ifInheritsStack as Stack<of IType>
545
546    cue init(token as IToken, idToken as IToken, box as Box, name as String, typeNode as ITypeProxy?, isNames as String*, initExpr as Expr?, attribs as AttributeList?, docString as String)
547        base.init(token, idToken, box, name, typeNode, isNames, initExpr, attribs, docString)
548        _ifInheritsStack = Stack<of IType>()
549        _useBackEndNameStack = Stack<of String>()  # for if-inherits
550        _useBackEndNameStack.push(name)
551
552    get englishName as String is override
553        return 'variable member'
554
555    pro ifInheritsStack from var
556
557    def attemptAssignmentOf(type as IType) as bool
558        # TODO: dup'ed with AbstractLocalVar
559        if _ifInheritsStack.count == 0
560            return false
561        stack = Stack<of IType>(Stack<of IType>(_ifInheritsStack))
562        assert stack.peek == _ifInheritsStack.peek
563        count = 0
564        post while stack.count and not type.isAssignableTo(stack.peek)
565            stack.pop
566            count += 1
567        if count or type.isAssignableTo(_type to !)
568            _ifInheritsStack = stack
569            # tell each IfStmt not to pop the if-inherits stack since the assignment did so
570            for node in .compiler.nodeStack
571                if node inherits IfStmt
572                    if node.ifInheritsVar is this
573                        node.doNotPopIfInheritsStack
574                        count -= 1
575                        if count == 0, break                       
576            return true
577        else
578            return false
579
580    def setInitExpr(initExpr as Expr) as bool
581        """ Used in property declaration when providing an initial value for an existing backing var. """
582        if _initExpr, return false
583        _initExpr = initExpr
584        return true 
585
586    ## INamedNode
587
588    get typeForReceiver as IType is override
589        return if(_ifInheritsStack.count, _ifInheritsStack.peek, .resultType)
590
591    ## IVar
592
593    pro type as IType? is override
594        get
595            return if(_ifInheritsStack.count, _ifInheritsStack.peek, _type)
596        set
597            assert false, 'Cannot set the type of a ClassVar.'
598
599
600interface HasAddStmt
601    def addStmt(stmt as Stmt)
602
603
604class AnonymousMethod
605    is partial
606    inherits AbstractMethod
607   
608    cue init(token as IToken, parentMethod as AbstractMethod, paramsList as List<of Param>, returnTypeNode as ITypeProxy?, isNames as String*)
609        base.init(token, token, parentMethod.parentBox, parentMethod, '(anonymous method)', paramsList, isNames, AttributeList(), '')
610        _name = '(anonymous method [.serialNum])'
611        _returnTypeNode = returnTypeNode
612        for param in .params, param.isAnonymousParam = true
613
614    get englishName as String is override
615        return 'anonymous method'
616
617    get hasReturnStmt as bool is override
618        # the second boolean term is to prevent an extra error in _bindImp for lambdas,
619        # although it's not clear to me why this extra guard is needed
620        return base.hasReturnStmt or (_stmts.count > 0 and _stmts[0] inherits ReturnStmt)
621       
622    get hasVariArgs as bool
623        return false
624
625    get isCallable as bool is override
626        return true
627
628    get isMethod as bool is override
629        return true
630
631    get shouldBeVirtual as bool is override
632        return false
633
634    def lambdaReturnType as IType
635        """ This is a service method for LambdaExpr. """
636        require .didBindInt and .didBindImp
637        stmt = _stmts[0]
638        if stmt inherits ReturnStmt
639            return stmt.expr.type to !
640        else
641            throw FallThroughException(stmt)
642
643    def _bindInt is override
644        base._bindInt
645        if _returnTypeNode is nil
646            _returnType = .compiler.passThroughType  # TODO: infer return type from return statement like C# does
647
648    def _bindImp is override
649        base._bindImp
650        numErrors = .compiler.errors.count
651        base._bindImp
652        if .compiler.errors.count == numErrors and _
653            _returnType not in [.compiler.voidType to IType, .compiler.passThroughType to IType] and _
654            not .hasReturnStmt and not .hasYieldStmt and not .hasThrowStmt and _
655            .bodyExclusion is nil
656            # then
657            .throwError('Missing return statement for method "[_name]" which returns [_returnType.name].')
658        hasYield = false
659        for stmt in _stmts
660            if stmt inherits YieldStmt
661                hasYield = true
662                break
663        if hasYield, .throwError('Cannot yield inside an anonymous method.')
664
665
666class AbstractMethod
667    is abstract, partial
668    inherits BoxMember
669    implements HasAddStmt, IOverloadable
670    """
671    The abstract ancestor class for
672        Initializer
673        Method
674        PropertyXetter
675
676    But not for Property (which has two code blocks "get" and "set"),
677    or other class decls like variables.
678
679    Subclasses must set _params.
680    """
681
682    var _parentMethod as AbstractMethod?
683    var _returnTypeNode as ITypeProxy?
684    var _returnType as IType?
685    var _requirePart as RequirePart?
686    var _ensurePart as EnsurePart?
687    var _oldExprs as List<of OldExpr>
688    var _params as IList<of Param>
689    var _locals as List<of LocalVar>
690    var _stmts as List<of Stmt>
691    var _hasReturnStmt as bool
692    var _hasThrowStmt as bool
693    var _hasYieldStmt as bool
694    var _curStmtIndex as int  # to implement .replaceChild
695    var _isCompilerGenerated as bool
696
697    cue init(token as IToken, idToken as IToken, box as Box, parentMethod as AbstractMethod?, name as String, paramsList as IList<of Param>, isNames as String*, attribs as AttributeList, docString as String)
698        base.init(token, idToken, box, name, isNames, attribs, docString)
699        _parentMethod = parentMethod
700        _params = paramsList.clone
701        _oldExprs = List<of OldExpr>()
702        _locals = List<of LocalVar>()
703        _stmts = List<of Stmt>()
704
705    get params as IList<of Param> is override
706        return _params
707
708    get locals from var
709
710    get isCallable as bool is override
711        return true
712
713    pro hasReturnStmt from var
714
715    pro hasThrowStmt from var
716
717    pro hasYieldStmt from var
718
719    pro isCompilerGenerated from var
720
721    def bodyExclusion as String?
722        ensure result in [nil, 'abstract', 'interface', 'extern box', 'dll import']
723        if 'abstract' in .isNames, return 'abstract'
724        if .parentBox inherits Interface and not .parentBox inherits Mixin, return 'interface'
725        if .parentBox.isExtern, return 'extern box'
726        for attrib in .attributes, if attrib.name in ['DllImport', 'DllImportAttribute'], return 'dll import'
727        return nil
728       
729    def addStmt(stmt as Stmt)
730        _stmts.add(stmt)
731
732    pro requirePart from var
733
734    pro ensurePart from var
735
736    get parentMethod from var
737        """
738        Anonymous methods have a .parentMethod.
739        In the future, there may be nested method declarations which will also then have a .parentMethod.
740        """
741
742    get resultType as IType is override
743        or require
744            .didBindInt
745            _returnType
746        body
747            return _returnType to !
748
749    get statements from _stmts
750
751    def findLocal(name as String) as AbstractLocalVar?
752        # TODO: should this use a dictionary lookup?
753        for local in _locals, if local.name==name, return local
754        for param in _params, if param.name==name, return param
755        if .parentMethod, return .parentMethod.findLocal(name)
756        else, return nil
757
758    def findLocalCI(name as String) as AbstractLocalVar?
759        name = name.toLower
760        # TODO: should this use a dictionary lookup?
761        for local in _locals, if local.name.toLower==name, return local
762        for param in _params, if param.name.toLower==name,  return param
763        if .parentMethod, return .parentMethod.findLocalCI(name)
764        else, return nil
765
766    def addLocal(local as LocalVar)
767        require
768            .findLocal(local.name) is nil
769        body
770            other = .findLocalCI(local.name)
771            if other, .throwError('Parameters and locals must vary by more than just their case. Change "[local.name]" or "[other.name]" to match, or rename one of them to something different.')
772            local.isTracked = true
773            _locals.add(local)
774   
775    def replaceLocal(local as LocalVar)
776        """
777        A utility method for EnsurePart to slip in the `result`.
778        """
779        require
780            .findLocal(local.name)
781        body
782            _locals.remove(.findLocal(local.name) to LocalVar)
783            _locals.add(local)
784
785    def removeLocal(name as String)
786        require
787            name.length
788            .findLocal(name)
789        body
790            for i = 0 .. _locals.count
791                if _locals[i].name==name
792                    _locals.removeAt(i)
793                    break
794
795    def addOldExpr(oldExpr as OldExpr)
796        _oldExprs.add(oldExpr)
797        oldExpr.sharpVarName = '_lh_old_' + _oldExprs.count.toString
798
799    get oldExprs from var
800        """
801        Use .addOldExpr(e) instead of method.oldExprs.add().
802        """
803
804    def changeToPublic
805        ensure
806            .isPublic
807            not .isInternal and not .isProtected and not .isPrivate
808            .isShared == old .isShared
809        body
810            for name in ['internal', 'private', 'protected'], _isNames.remove(name)
811            if 'public' not in _isNames, _isNames.add('public')
812
813    def changeToNonVirtual
814        ensure
815            'virtual' not in .isNames
816            'nonvirtual' in .isNames
817            .isShared == old .isShared
818        body
819            _isNames.remove('virtual')
820            if 'nonvirtual' not in _isNames, _isNames.add('nonvirtual')
821
822    def unNilReturnType is override
823        if _returnType
824            if _returnType inherits NilableType
825                _returnType = _returnType.nonNil
826        else if _returnTypeNode inherits NilableTypeProxy
827            _returnTypeNode = _returnTypeNode.innerTypeProxy
828       
829    def addRefFields is override
830        base.addRefFields
831        .addField('returnTypeNode', _returnTypeNode)
832        .addField('returnType', _returnType)
833
834    def addSubFields is override
835        base.addSubFields
836        .addField('params', _params)
837        .addField('requirePart', _requirePart)
838        .addField('ensurePart', _ensurePart)
839        .addField('locals', _locals)
840        .addField('stmts', _stmts)
841
842    def cobraSourceSignature as String is override
843        return .cobraSourceSignature(true)
844
845    def cobraSourceSignature(includeShared as bool) as String
846        sb = StringBuilder('def [.name]')
847        if .params.count > 0
848            sb.append('(')
849            sep = ''
850            for param in .params
851                sb.append('[sep][param.name] as ')
852                branch param.direction
853                    on Direction.In, pass
854                    on Direction.InOut, sb.append('inout ')
855                    on Direction.Out, sb.append('out ')
856                sb.append('[param.type.name]')
857                sep = ', '
858            sb.append(')')
859        if not .resultType inherits VoidType
860            sb.append(' as [.resultType.name]')
861            if includeShared and .isShared
862                # TODO: other is names
863                sb.append(' is shared')
864        return sb.toString
865
866    get canHaveDetailedStackTrace as bool
867        return .parentBox.canHaveDetailedStackTrace and not .hasYieldStmt
868
869    get willRequire as bool
870        # in the future, this will also depend on a command line option to include/exclude preconditions
871        # classes that are sensitive with respect to the DST skip on contracts
872        return .canHaveDetailedStackTrace
873
874    get willEnsure as bool
875        # in the future, this will also depend on a command line option to include/exclude preconditions
876        return .canHaveDetailedStackTrace
877
878    def replaceChild(find as INode, replace as INode) as bool
879        if _curStmtIndex < _stmts.count and _stmts[_curStmtIndex] is find
880            _stmts[_curStmtIndex] = replace to Stmt
881            return true
882        else
883            return base.replaceChild(find, replace)
884
885    def _stackPush
886        base._stackPush
887        .compiler.codeMemberStack.push(this)
888
889    def _stackPop
890        base._stackPop
891        .compiler.codeMemberStack.pop
892       
893    def _bindInt is override
894        assert .compiler.codeMemberStack.peek is this
895        base._bindInt
896        assert .parentBox.didUnNilReturnTypes
897        for param in _params, param.bindInt  # TODO: add error recovery
898        if _returnType is nil
899            if _returnTypeNode, _returnType = _returnTypeNode.realType
900            else, _returnType = .compiler.voidType
901            assert _returnType, _returnTypeNode
902        if _requirePart is nil and .willRequire, _requirePart = RequirePart(this)
903        if _requirePart, _requirePart.bindInt
904        if _ensurePart is nil and .willEnsure, _ensurePart = EnsurePart(this)
905        if _ensurePart, _ensurePart.bindInt
906
907    def _bindImp is override
908        assert .compiler.codeMemberStack.peek is this
909        base._bindImp
910        params = .params
911        if .matchingBaseMember
912            matchingBaseMember = .matchingBaseMember
913            if 'override' in .isNames and 'private' in matchingBaseMember.isNames
914                .recordError('Cannot override the base member which is declared "private".')
915            for i in params.count
916                param = params[i]
917                if matchingBaseMember.params[i].type inherits NilableType and not param.type inherits NilableType
918                    .recordError('Cannot make param "[param.name]" non-nil because the base method declares it is nilable. Change "[param.name]" to "[matchingBaseMember.params[i].type.name]", or change the base method.')
919        for param in params
920            param.bindImp
921        for local in _locals
922            local.bindImp
923        if _requirePart
924            _requirePart.bindImp
925        if _ensurePart and _returnType and _returnType is not .compiler.voidType
926            _backEndResultVarName = '_lh_result'
927        else
928            _backEndResultVarName = ''
929        _curStmtIndex = 0
930        for stmt in _stmts.toArray
931            try
932                stmt.bindImp
933                stmt.afterStatementBindImp  # to let expressions know when they are used as statements
934            catch ne as NodeException
935                .compiler.recordError(ne)
936            _curStmtIndex += 1
937        for param in _params, assert param.ifInheritsStack.count == 0
938        if _ensurePart, _ensurePart.bindImp
939        if _requirePart, _requirePart.checkConnectToken
940        if _ensurePart, _ensurePart.checkConnectToken
941        .checkForUnmarkedOverrideOrNew  # have to do this after statements bindImp since using base can add `override`
942        .checkForUnusedParams
943        .checkForUnusedVariables
944
945    var _didComputeMatchingBaseMember = true
946   
947    get didComputeMatchingBaseMember from var
948
949    def _computeMatchingBaseMembers
950        and ensure .matchingBaseMember is not this
951        base._computeMatchingBaseMembers
952        if not .canHaveMatchingBaseMember, return
953        _didComputeMatchingBaseMember = true
954        if _matchingBaseMember, return
955        if not .canHaveMatchingBaseMember, return
956        if (parentBox = .parentBox) inherits Class
957            if parentBox.baseClass
958                baseMember = parentBox.baseClass.memberForName(_name)
959                if baseMember is nil
960                    pass
961                else if baseMember inherits AbstractMethod
962                    if .matchesSignature(baseMember)
963                        _matchingBaseMember = baseMember
964                else if baseMember inherits MemberOverload
965                    if baseMember.members[0] inherits AbstractMethod
966                        for member in baseMember.members
967                            if .matchesSignature(member)
968                                _matchingBaseMember = member
969                    else
970                        cannotRedeclare = true
971                else
972                    cannotRedeclare = true
973                if cannotRedeclare
974                    .recordError('Cannot declare a [.englishName] named "[.name]" because the base member "[.name]" is a [baseMember.englishName].')
975                if _matchingBaseMember and _matchingBaseMember.parentBox inherits Interface
976                    # not really a base member in the "base class" sense of the word.
977                    # would not require marking as "is override" for example
978                    _matchingBaseMember = nil
979
980    def checkForUnmarkedOverrideOrNew
981        require .didComputeMatchingBaseMember
982        if 'new' not in .isNames and 'override' not in .isNames and _matchingBaseMember and not _matchingBaseMember.isExtensionMember
983            .throwError('Member "[_name]" also exists in the base class. You must specify "is override" or "is new", or change the name.')
984
985    def checkForUnusedParams
986        pass
987        /#
988        not yet. see http://cobra-language.com/trac/cobra/ticket/279
989        if not .isAbstract and .statements.count
990            for param in _params
991                if not param.isUsed and not param.isOut and not param.isDeclaredAsUnused
992                    .compiler.warning(param, 'The value of parameter "[param.name]" is never used in [.parentBox.name].[.name].')
993        #/
994
995    def checkForUnusedVariables
996        for local in _locals
997            if not local.isUsed
998                .compiler.warning(local, 'The value of variable "[local.name]" is never used.')
999
1000
1001    ## Generics
1002
1003    def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember  # CC: as same
1004        newMember = base.constructedFor(box, gpToType) to AbstractMethod  # CC: this = base. ...
1005        assert newMember._returnType
1006        newMember._returnType = newMember._returnType.secondaryConstructedTypeFor(box, gpToType)
1007        newMember._params = for p in _params get p.constructedFor(box, gpToType)
1008        return newMember
1009
1010
1011class Initializer inherits AbstractMethod is partial
1012
1013    cue init(token as IToken, idToken as IToken, box as Box, paramsList as IList<of Param>, isNames as String*, attribs as AttributeList, docString as String)
1014        base.init(token, idToken, box, nil, 'cue.init', paramsList, isNames, attribs, docString)
1015        _isNames = isNames.toList
1016        _stmts = List<of Stmt>()
1017
1018    get englishName as String is override
1019        return 'initializer'
1020
1021    get hasVariArgs as bool
1022        assert false, 'TODO: complete Initializer.hasVariArgs'
1023        return false
1024
1025    get isCallable as bool is override
1026        return true
1027
1028    get isMethod as bool is override
1029        return true  # because this is needed for "base.init" without ()s
1030
1031    def usesBase is override
1032        # initializers cannot be virtual and don't require "is new"
1033        pass
1034
1035    def checkForUnmarkedOverrideOrNew is override
1036        # initializers don't say override or new
1037        pass
1038
1039    get lastToken as IToken
1040        if _stmts.count
1041            return _stmts[_stmts.count-1].lastToken  # CC: use .last
1042        else
1043            return .token
1044
1045    def mangleName(name as String) as String is override
1046        if name == 'init', return 'cue.init'
1047        else, return name
1048
1049    def _bindImp
1050        if .canHaveDetailedStackTrace  # no extra checks for classes that are sensitive with respect to the DST
1051            # add asserts at the end for non-nilable class vars
1052            if .compiler.options.boolValue('include-nil-checks')
1053                token = .lastToken.copy
1054                token.incLineNum
1055                stmts = List<of Stmt>()
1056                for decl in .parentBox.declsInOrder
1057                    if decl inherits BoxVar
1058                        assert decl.type
1059                        if not decl.type inherits NilableType and decl.type.isReference and not decl.isShared
1060                            if decl.name.startsWith('_')
1061                                expr = IdentifierExpr(token, decl) to Expr
1062                            else
1063                                expr = DotExpr(token, 'DOT', ThisLit(token, isImplicit=true), MemberExpr(token, decl.name), isImplicit=true) to Expr
1064                            stmts.add(AssertStmt(token, IsNotNilExpr(token, expr), nil))
1065                if stmts.count
1066                    cond = BinaryOpExpr.make( _
1067                        .lastToken.copy('DOT', '.'), 'DOT', _
1068                        IdentifierExpr(.lastToken.copy('ID', 'CobraCore')), _
1069                        MemberExpr(.lastToken.copy('ID', '_willCheckNil')))
1070                    block = BlockStmt(token, stmts)
1071                    ifStmt = IfStmt(token, cond, block, nil)
1072                    .addStmt(ifStmt)
1073
1074        base._bindImp
1075
1076        first = true
1077        for stmt in _stmts
1078            isBase = isThisInit = false
1079            if stmt inherits DotExpr
1080                if stmt.left inherits BaseLit
1081                    isBase = true
1082                else if stmt.left inherits ThisLit and ((stmt.right inherits MemberExpr and (stmt.right to MemberExpr).name == 'init') or (stmt.right inherits CallExpr and (stmt.right to CallExpr).name == 'init'))  # CC: axe casts
1083                    isThisInit = true
1084            if first
1085                if .isClassMember and not isBase and not isThisInit and not .isShared
1086                    # structs cannot call base constructors
1087                    # static initializers cannot call base constructors
1088                    .compiler.warning(stmt, 'The first statement of an "init" cue should be a call to another "init" in this class or the base class.')
1089            else
1090                if isBase
1091                    # TODO: after switching to byte code gen, it would be nice to relax this a bit, by say allowing statements that use args and locals, but can't set class state by, for example, setting class vars or calling members
1092                    stmt.recordError('The base initializer can only be invoked in the first statement.')
1093                else if isThisInit
1094                    stmt.recordError('Another initializer can only be invoked in the first statement.')
1095            first = false
1096               
1097
1098class Method inherits AbstractMethod is partial
1099
1100    var _implementsTypeNode as ITypeProxy?
1101    var _implementsType as IType?
1102
1103    cue init(token as IToken, idToken as IToken, box as Box, name as String, paramsList as IList<of Param>, returnTypeNode as ITypeProxy, implementsTypeNode as ITypeProxy?, isNames as String*, attribs as AttributeList, docString as String)
1104        .init(token, idToken, box, name, List<of IType>(), paramsList, returnTypeNode, implementsTypeNode, isNames, attribs, docString)
1105
1106    cue init(token as IToken, idToken as IToken, box as Box, name as String, genericParams as List<of IType>, paramsList as IList<of Param>, returnTypeNode as ITypeProxy, implementsTypeNode as ITypeProxy?, isNames as String*, attribs as AttributeList, docString as String)
1107        base.init(token, idToken, box, nil, name, paramsList, isNames, attribs, docString)
1108        _genericParams = genericParams
1109        for gp in _genericParams, if gp inherits GenericParam, gp.parentDefinition = this
1110        _returnTypeNode = returnTypeNode
1111        _implementsTypeNode = implementsTypeNode
1112        if 'virtual' in _isNames and _implementsTypeNode
1113            _isNames.remove('virtual')
1114        _stmts = List<of Stmt>()
1115
1116    get englishName as String is override
1117        return 'method'
1118
1119    get hasVariArgs as bool
1120        assert false, 'TODO: complete Method.hasVariArgs'
1121        return false
1122
1123    get isCallable as bool is override
1124        return true
1125
1126    pro isMain from var as bool
1127
1128    pro hasMainParams from var as bool
1129        """ Flag that this method needs main entry point params inserted. Used by BackEnd codeGen"""
1130   
1131    get isMethod as bool is override
1132        return true
1133
1134    get implementsType from var
1135
1136    get shouldBeVirtual as bool is override
1137        return .isClassMember and not .isPrivate and not _implementsTypeNode and not _implementsType
1138
1139    def addRefFields is override
1140        base.addRefFields
1141        .addField('implementsType', _implementsType)
1142
1143    def addSubFields is override
1144        base.addSubFields
1145        .addField('genericParams', _genericParams)
1146        if _implementsTypeNode
1147            .addField('implementsTypeNode', _implementsTypeNode)
1148
1149    get defaultAccessLevel as String is override
1150        dal = base.defaultAccessLevel
1151        if dal.length and not _implementsType and not _implementsTypeNode
1152            return dal
1153        else
1154            return ''
1155
1156
1157    ## Generics
1158
1159    var _genericParams as List<of IType>
1160    var _genericDef as Method?  # non-nil for a method constructed from a generic method
1161    var _constructions as Dictionary<of String, Method>?
1162
1163    get genericDef from var
1164
1165    get genericParams from var
1166
1167    get isConstructed as bool
1168        return _genericDef is not nil
1169
1170    get isGenericDef as bool
1171        """
1172        Returns true if this type is a definition of a generic type, and
1173        therefore capable of making subsequent generic types via the
1174        `constructedTypeFor` method. A generic type will have generic
1175        parameters which are all GenericParam (as opposed to other types
1176        such as `int` or a given class).
1177        """
1178        # TODO: make this more efficient, maybe by computing in the initializer
1179        if .genericDef or .genericParams.count == 0
1180            return false
1181        else if .genericParams.count
1182            for param in .genericParams
1183                if not param inherits GenericParam
1184                    return false
1185            return true
1186        else
1187            return false
1188
1189    get containsGenericParameters as bool
1190        for param in .genericParams
1191            if param inherits GenericParam
1192                return true
1193            if param inherits Box  # CC: and <next if condition>
1194                if param.containsGenericParameters
1195                    return true
1196        return false
1197
1198    def constructedMethodWith(typeArgs as List<of IType>) as Method
1199        """
1200        This is for methods that are generic themselves due to having their own generic parameters.
1201        For methods that simply belong to a generic class, see .constructedFor.
1202       
1203        TODO: Consider the case of a method that implements a specific interface. Can that have generic parameters?
1204        """
1205        require
1206            .didBindInt
1207            .isGenericDef
1208            .containsGenericParameters
1209            typeArgs.count == .genericParams.count
1210        ensure
1211            typeArgs <> .genericParams implies this is not result
1212            result.genericParams == typeArgs
1213        body
1214            if typeArgs == _genericParams
1215                return this
1216
1217            # the cache of constructed types has to come from the root generic type def or you end up with duplicate
1218            # constructed types which is not only wasteful, but causes problems with inheritance tests
1219            genericDef = this
1220            while genericDef.genericDef
1221                genericDef = genericDef.genericDef to !
1222
1223            if genericDef._constructions is nil
1224                genericDef._constructions = Dictionary<of String, Method>()
1225
1226            key = TypeUtil.keyForTypeArgs(typeArgs)
1227
1228            if genericDef._constructions.containsKey(key)
1229                return genericDef._constructions[key]
1230
1231            c = .memberwiseClone to Method
1232            assert c is not this
1233            genericDef._constructions[key] = c
1234            c._genericDef = this
1235            c._constructions = nil  # only the generic def tracks the constructions         
1236            c._genericParams = List<of IType>(typeArgs)
1237            c._params = List<of Param>()
1238            typeForParam = TypeUtil.dictionaryOf(.genericParams, typeArgs)
1239            for param in .params
1240                c._params.add(param.constructedFor(.parentBox, typeForParam))
1241            c._returnType = c._returnType.secondaryConstructedTypeFor(.parentBox, typeForParam)
1242            c.bindInt
1243            return c
1244
1245    def genericParamForName(name as String) as IType?
1246        # idea: return find param in _genericParams where param.name == name
1247        for param in _genericParams, if param.name == name, return param
1248        return nil
1249
1250
1251    ## Binding
1252
1253    def _bindInt is override
1254        assert .compiler.codeMemberStack.peek is this
1255        for param in _genericParams, param.bindInt
1256        base._bindInt
1257        if .name == '__init__', .compiler.warning(this, 'Initializers are named "init" with no surrounding underscores.')
1258        if not 'extern' in .isNames and .bodyExclusion == 'dll import', _isNames.add('extern')
1259
1260    def _bindImp is override
1261        numErrors = .compiler.errors.count
1262        base._bindImp
1263        for param in _genericParams
1264            param.bindImp
1265        if _implementsTypeNode
1266            _implementsType = _implementsTypeNode.realType
1267            # TODO: make sure the type is among the interfaces of the box
1268        if .compiler.errors.count == numErrors and _
1269            _returnType not in [.compiler.voidType to IType, .compiler.passThroughType to IType] and _
1270            not .hasReturnStmt and not .hasYieldStmt and not .hasThrowStmt and _
1271            .bodyExclusion is nil
1272            # nil
1273            .throwError('Missing return statement for method "[_name]" which returns [_returnType.name].')
1274        # check for `return` and `yield` in the same method
1275        if .resultType is not .compiler.voidType
1276            returnStmts = List<of Stmt>()
1277            hasYield = false
1278            for stmt in _stmts
1279                if stmt inherits ReturnStmt
1280                    returnStmts.add(stmt)
1281                else if stmt inherits YieldStmt
1282                    hasYield = true
1283            if hasYield and returnStmts.count
1284                for stmt in returnStmts
1285                    stmt.recordError('Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.')
1286
1287
1288class ProperDexer
1289    is abstract, partial
1290    inherits BoxMember
1291    """
1292    The abstract base class for Property and Indexer.
1293
1294    This captures some of the common interface between them--whatever happens to be needed.
1295
1296    Why not do an IProperDexer interface? Because it wouldn't be compatible with a BoxMember
1297    unless we also defined an IBoxMember.
1298    """
1299
1300    var _getPart as ProperDexerXetter?
1301    var _setPart as ProperDexerXetter?
1302    var _coverVar as BoxVar?
1303    var _coverAccess as String?
1304    var _returnType as IType?
1305    var _returnTypeNode as ITypeProxy?
1306
1307    cue init(token as IToken, idToken as IToken, box as Box, name as String, isNames as String*, attribs as AttributeList, docString as String?)
1308        base.init(token, idToken, box, name, isNames, attribs, docString)
1309
1310    cue init(token as IToken, idToken as IToken, box as Box, name as String, returnTypeOrNode as INode, isNames as String*, attribs as AttributeList, docString as String)
1311        .init(token, idToken, box, name, isNames, attribs, docString)
1312        if returnTypeOrNode inherits IType
1313            _returnType = returnTypeOrNode
1314            _returnTypeNode = nil
1315        else if returnTypeOrNode inherits ITypeProxy
1316            _returnType = nil
1317            _returnTypeNode = returnTypeOrNode
1318        else
1319            throw FallThroughException(returnTypeOrNode)
1320
1321    def makeGetPart(token as IToken) as AbstractMethod is abstract
1322        require .getPart is nil
1323        ensure .getPart
1324
1325    def makeSetPart(token as IToken) as AbstractMethod is abstract
1326        require .setPart is nil
1327        ensure .setPart
1328
1329    def addRefFields is override
1330        base.addRefFields
1331        if _coverVar, .addField('coverVar', _coverVar)
1332        if _returnType, .addField('returnType', _returnType)
1333        else, .addField('returnTypeNode', _returnTypeNode)
1334
1335    def addSubFields is override
1336        base.addSubFields
1337        if _getPart, .addField('getPart', _getPart)
1338        if _setPart, .addField('setPart', _setPart)
1339
1340    get returnType from var
1341
1342    get returnTypeNode from var
1343
1344    get resultType as IType is override
1345        if not .didBindInt, .bindInt  # to call a method from a var/const initializer
1346        assert .didBindInt
1347        assert _returnType
1348        return _returnType to !
1349
1350    get getPart from var
1351
1352    get setPart from var
1353
1354    get shouldBeVirtual as bool is override
1355        return .isClassMember and not .isPrivate  # TODO: and not _implementsTypeNode and not _implementsType
1356
1357    def mergedIntoPartialBox(newBox as Box)
1358        base.mergedIntoPartialBox(newBox)
1359        if .getPart, .getPart.mergedIntoPartialBox(newBox)
1360        if .setPart, .setPart.mergedIntoPartialBox(newBox)
1361
1362    def unNilReturnType is override
1363        if _returnType
1364            if _returnType inherits NilableType
1365                _returnType = _returnType.nonNil
1366        else if _returnTypeNode inherits NilableTypeProxy
1367            _returnTypeNode = _returnTypeNode.innerTypeProxy
1368
1369    def _bindInt is override
1370        base._bindInt
1371        if _returnType is nil
1372            if _coverVar
1373                _coverVar.bindInt
1374                _returnType = _coverVar.resultType
1375            else
1376                assert _returnTypeNode
1377                _returnType = _returnTypeNode.realType
1378        assert _returnType  # properties always have a return type
1379
1380        if _coverVar
1381            if _coverAccess in ['getset', 'get']
1382                # return VARNAME
1383                .makeGetPart(.token).statements.add(ReturnStmt(.token, IdentifierExpr(.token, _coverVar)))
1384            if _coverAccess in ['getset', 'set']
1385                # VARNAME = value
1386                .makeSetPart(.token).statements.add(AssignExpr(.token, 'ASSIGN', IdentifierExpr(.token, _coverVar), IdentifierExpr(.token, 'value')))
1387
1388        if _getPart, _getPart.bindInt
1389        if _setPart, _setPart.bindInt
1390
1391    def _computeMatchingBaseMembers
1392        base._computeMatchingBaseMembers
1393        if _getPart, _getPart.computeMatchingBaseMembers
1394        if _setPart, _setPart.computeMatchingBaseMembers
1395
1396    def _bindImp is override
1397        base._bindImp
1398        if _getPart, _getPart.bindImp
1399        if _setPart, _setPart.bindImp
1400
1401    def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember  # CC: as same
1402        newMember = base.constructedFor(box, gpToType) to ProperDexer  # CC: this = base. ...
1403        assert newMember._returnType
1404        newMember._returnType = newMember._returnType.secondaryConstructedTypeFor(box, gpToType)
1405        return newMember
1406
1407
1408class ProperDexerXetter
1409    is abstract, partial
1410    inherits AbstractMethod
1411    """
1412    Base class for IndexerGetter, IndexerSetter, PropertyGetter and PropertySetter.
1413    """
1414
1415    var _parent as ProperDexer
1416
1417    cue init(token as IToken, parent as ProperDexer, isNames as String*)
1418        base.init(token, token, parent.parentBox, nil, parent.name, List<of Param>(), isNames, AttributeList(), '')  # TODO: should be docString, right?
1419        _name = parent.name + '.' + .xetPartName  # CC: somewhat awkward. belongs in the base.init() call
1420        _parent = parent
1421
1422    get canHaveMatchingBaseMember as bool is override
1423        return _parent.canHaveMatchingBaseMember
1424
1425    get parent from var
1426
1427    get xetPartName as String is abstract
1428
1429    get isNames as String* is override
1430        # may need to get more sophisticated here if, in the future, we support C#'s somewhat recent ability to put access modifiers on just the set or get part of a property
1431        return _parent.isNames
1432
1433    get isShared as bool is override
1434        return _parent.isShared
1435
1436    get isOverride as bool is override
1437        return _parent.isOverride
1438
1439    def usesBase is override
1440        # it's the property that needs to compute the "is override" or "is new" for itself,
1441        # not the get or set part
1442        _parent.usesBase
1443
1444
1445class MemberOverload inherits BoxMember is partial
1446    """
1447    Methods and indexers can be overloaded.
1448    """
1449   
1450    var _members as List<of IOverloadable>
1451
1452    cue init(member as IOverloadable)
1453        require
1454            member.name.length
1455        ensure
1456            .members.count
1457            .members[0] == member
1458        body
1459            base.init(member.token, member.idToken, member.parentBox, member.name, member.isNames, AttributeList())
1460            member.overloadGroup = this
1461            _members = [member]
1462            _isNames = member.isNames.toList  # pick up 'shared' for example
1463
1464    def addMinFields is override
1465        base.addMinFields
1466        .addField('numMembers', .members.count)
1467       
1468    def addSubFields is override
1469        base.addSubFields
1470        .addField('members', _members)
1471
1472    get members from var
1473
1474    get englishName as String is override
1475        assert _members.count
1476        return _members[0].englishName
1477
1478    get isCallable as bool is override
1479        assert _members.count
1480        return _members[0].isCallable
1481
1482    get isMethod as bool is override
1483        return _members[0].isMethod
1484
1485    def addMember(member as IOverloadable)
1486        require
1487            member.overloadGroup is nil
1488            member.name==.name
1489            member not in .members
1490            member.getType is .members[0].getType
1491        body
1492            member.overloadGroup = this
1493            _members.add(member)
1494            .parentBox.addDeclFromOverload(member, this)
1495
1496    def addInheritedMember(member as IOverloadable)
1497        require
1498            member.name==.name
1499            member.parentBox is not .parentBox
1500            member not in .members
1501            member.getType is .members[0].getType
1502        body
1503            _members.add(member)
1504
1505    def addInheritedMemberIfNoMatch(member as IOverloadable)
1506        """
1507        Utility method for _finishOverloads to avoid the mistake of putting an ancestor method in an overload that was an override or new.
1508        """
1509        for existing in _members, if member.matchesSignature(existing), return
1510        .addInheritedMember(member)
1511           
1512    get resultType as IType? is override
1513        assert .didBindInt
1514        return _members[0].resultType
1515
1516    def unNilReturnType is override
1517        for member in _members
1518            member.unNilReturnType
1519
1520    def computeBestOverload(args as List<of Expr>, genericArgTypes as List<of IType>?, strictBindChecking as bool) as IOverloadable
1521        # References:
1522        # http://blogs.msdn.com/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx
1523
1524        # I cooked up the algorithm below as a quick way to fix some bugs caused by the previous
1525        # implementation of um, doing nothing.
1526        # But this likely needs to be rewritten.
1527        candidates = []
1528        # handle generic arguments to the method       
1529        if genericArgTypes and genericArgTypes.count
1530            members = List<of IOverloadable>()
1531            for member as IOverloadable? in .members
1532                if member inherits Method
1533                    if member.containsGenericParameters
1534                        if member.genericParams.count == genericArgTypes.count
1535                            member = member.constructedMethodWith(genericArgTypes to !)
1536                        else
1537                            member = nil
1538                if member, members.add(member)
1539        else
1540            members = .members
1541
1542        _calcMembersScore(members, args, strictBindChecking, candidates)
1543           
1544        maxScore = -10_000
1545        winner = nil to IOverloadable?
1546        for pair in candidates
1547            if false, print pair[0], (pair[1] to IOverloadable).idString
1548            if pair[0] to int > maxScore
1549                maxScore = pair[0] to int
1550                winner = pair[1] to IOverloadable
1551
1552        if false
1553            # detect overload invocation ambiguity
1554            # TODO: not ready for this yet
1555            count = 0
1556            for pair in candidates
1557                if pair[0] to int == maxScore
1558                    count += 1
1559            if count > 1
1560                # TODO: do this for indexing too
1561                msg = 'The call is ambiguous between these methods: '
1562                sep = ''
1563                for pair in candidates
1564                    if pair[0] to int == maxScore
1565                        msg += sep + (pair[1] to IOverloadable).cobraSourceSignature(false)
1566                        sep = ', '
1567                .throwError(msg)
1568
1569        if false
1570            print
1571            trace .token.fileName
1572            trace maxScore, _name
1573            trace .token.toTechString
1574            trace winner
1575            print 'args:'
1576            for arg in args, print '   [arg]'
1577            print 'params:'
1578            for param in winner.params, print '   [param]'
1579            print 'overloads:'
1580            for member in .members, print '   [member]'
1581        # print 'winner:', score, winner
1582        assert winner
1583        return winner to !
1584   
1585    def _calcMembersScore(members as IList<of IOverloadable>, args as List<of Expr>, strictBindChecking as bool, candidates)
1586        for member in members
1587            score = -1000
1588            if member.params.count == args.count
1589                score = 0
1590                if member inherits Method and (member to Method).genericParams.count > 0, score += 1
1591                for i, param in member.params.numbered
1592                    arg = args[i]
1593                    if strictBindChecking and not arg.didBindImp
1594                        trace arg
1595                        trace arg.hasError
1596                    if strictBindChecking and (arg.type == param.type or arg.type == param.type.nonNil)
1597                        score += 20
1598                    else if strictBindChecking and arg.canBeAssignedTo(param.type)
1599                        score += 10
1600                    else if strictBindChecking and arg.type.nonNil.isAssignableTo(param.type) 
1601                        # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore
1602                        # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now
1603                        # compensate here.
1604                        score += 1
1605                    else if not strictBindChecking
1606                        if arg.type is not nil
1607                            if arg.type == param.type
1608                                score += 20
1609                            else if arg.type.isDynamic
1610                                score += 10
1611                        else
1612                            score -= 100
1613                    else
1614                        score -= 100
1615            # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name))
1616            candidates.add([score, member])
1617
1618    def _bindInt is override
1619        base._bindInt
1620        # sanity check that members all have the right name
1621        name = .name
1622        for member in _members, assert member.name == name
1623        # sanity check that members don't have the same signature
1624        #if name <> 'getType'  # TODO: HACK: See Box.prepSystemObjectClass
1625        # TODO: having problems with explicit interface implementation. I think these probably should have their name changed from "foo" to "Type.foo"
1626        #   for i = 0 .. _members.count
1627        #       for j = i+1 .. _members.count
1628        #           assert not _members[i].matchesSignature(_members[j])
1629        # rename the test members of the overload by suffixing them with '_o1' '_o2' etc.
1630
1631        for i, member in _members.numbered
1632            for tm in member.testMethods, tm.overloadId = i+1
1633
1634        # TODO: error if the two members have the same arguments
1635
1636    def _computeMatchingBaseMembers
1637        base._computeMatchingBaseMembers
1638        for member in _members, member.computeMatchingBaseMembers
1639
1640    def _bindImp is override
1641        base._bindImp
1642
1643
1644class TestMethod
1645    is partial
1646    inherits Method
1647    """
1648    TODO: Does TestMethod need to be altered in light of generic parameters to methods?
1649    """
1650
1651    var _forMember as BoxMember?
1652    var _forBox as Box?
1653    var _overloadId = -1
1654
1655    cue init(token as IToken, member as BoxMember)
1656        require
1657            member.name.length
1658        body
1659            base.init(token, Token.empty, member.parentBox, 'test_'+member.name.capitalized, List<of Param>(), .getCompiler.voidType, nil, ['shared'], AttributeList(), '')
1660            _forMember = member
1661
1662    cue init(token as IToken, box as Box)
1663        require
1664            box.name.length
1665        body
1666            base.init(token, Token.empty, box, 'test_class_'+box.name.capitalized, List<of Param>(), .getCompiler.voidType, nil, ['shared'], AttributeList(), '')
1667            _forBox = box
1668
1669    get shouldBeVirtual as bool is override
1670        return false
1671
1672    pro name as String is new
1673        """
1674        NamedNode.name does not allow the name to be set because that could mess up dictionaries
1675        that index the node by name. However, there are no such dictionaries for TestMethod and
1676        MemberOverload() needs to mangle test method names so they don't collide.
1677        """
1678        get
1679            return base.name
1680        set
1681            require value.length
1682            _name = value
1683
1684    pro overloadId from var
1685
1686
1687class ContractPart
1688    is abstract, partial
1689    inherits SyntaxNode
1690    """
1691    The abstract base class for RequirePart and EnsurePart.
1692    """
1693
1694    var _connectToken as IToken?
1695    var _codeMember as AbstractMethod
1696    var _exprs as List<of Expr>
1697
1698    cue init(codeMember as AbstractMethod)
1699        base.init(codeMember.token)
1700        .isImplicit = true
1701        _codeMember = codeMember
1702        _exprs = List<of Expr>()
1703
1704    cue init(connectToken as IToken?, mainToken as IToken, codeMember as AbstractMethod, exprs as List<of Expr>)
1705        base.init(mainToken)
1706        _connectToken = connectToken
1707        _codeMember = codeMember
1708        _exprs = exprs
1709
1710    def addMinFields
1711        base.addMinFields
1712        .addField('connectToken', _connectToken)
1713
1714    def addSubFields
1715        base.addSubFields
1716        .addField('exprs', _exprs)
1717
1718    get codeMember from var
1719
1720    get connectWord as String is abstract
1721
1722    get exprs from var
1723        has Subnodes
1724
1725    get haveConditions as bool is abstract
1726        """
1727        Returns true if this contract part has any conditions, whether directly or through inheritance.
1728        """
1729
1730    get willGenerateCode as bool
1731        # classes that are sensitive with respect to the DST skip on contracts
1732        return _codeMember.canHaveDetailedStackTrace and .compiler.options['contracts'] <> 'none' and not (.willInlineCode and not .haveConditions)
1733
1734    get willInlineCode as bool
1735        return .compiler.options['contracts'] == 'inline'
1736
1737    def _bindImp is override
1738        base._bindImp
1739        # note: expressions cannot refer to locals in contracts
1740        newExprs = List<of Expr>()
1741        for expr in _exprs
1742            expr.bindImp  # TODO: error recovery
1743            assert expr.type
1744            if not expr.hasError, _checkForLocalReferences(expr)
1745            if expr.type is not .compiler.boolType
1746                expr = TruthExpr(expr).bindAll to Expr # CC: axe cast when Cobra supports "as this"
1747                assert expr.type
1748            newExprs.add(expr)
1749        _exprs.clear
1750        _exprs.addRange(newExprs)
1751
1752    def _checkForLocalReferences(expr as Expr)
1753        validDefs = Set<of dynamic>()
1754        lastExpr as Expr?
1755        for expr in expr.allExprs
1756            if expr inherits IdentifierExpr
1757                if expr.definition inherits LocalVar
1758                    if (expr.name <> 'result' or not expr.definition.isImplicit) and (expr.definition not in validDefs)
1759                        if lastExpr inherits ForExpr
1760                            .flagVar(expr.definition to LocalVar)
1761                            validDefs.add(expr.definition)
1762                        else
1763                            expr.recordError('Cannot refer to local variable "[expr.name]" in a contract. Use an "assert" statement in the implementation to verify conditions on locals.')
1764            lastExpr = expr
1765
1766    def flagVar(varr as LocalVar) is abstract
1767
1768    def checkConnectToken
1769        """
1770        This method checks that you said 'or require' vs. 'require', or 'and ensure' vs. 'ensure'.
1771        The check cannot be made during _bindImp because it relies on the .codeMember.isNames which can be altered when binding the statements in the method body (per .usesBase).
1772        """
1773        isNames = _codeMember.isNames
1774        if not .isImplicit
1775            if _connectToken
1776                if 'override' not in isNames
1777                    .recordError('The contract was specified with "[_connectToken.text]", but the code member is neither an "override" nor "new".')
1778            else
1779                if 'override' in isNames
1780                    what = if('override' in isNames, 'override', 'new')
1781                    .recordError('The contract must be declared "[.connectWord] [.token.text]" rather than "[.token.text]" because the code member is "[what]".')
1782
1783
1784class RequirePart
1785    is partial
1786    inherits ContractPart
1787
1788    cue init(codeMember as AbstractMethod)
1789        base.init(codeMember)
1790
1791    cue init(connectToken as IToken?, mainToken as IToken, codeMember as AbstractMethod, exprs as List<of Expr>)
1792        base.init(connectToken, mainToken, codeMember, exprs)
1793        codeMember.requirePart = this
1794
1795    get connectWord as String is override
1796        return 'or'
1797
1798    def flagVar(varr as LocalVar) is override
1799        varr.isForRequire = true
1800
1801    get haveConditions as bool is override
1802        if _codeMember.isOverride
1803            have = false
1804            curCodeMember = _codeMember to ?
1805            post while curCodeMember
1806                if curCodeMember.requirePart.exprs.count
1807                    have = true
1808                    break
1809                curCodeMember = curCodeMember.matchingBaseMember to AbstractMethod?
1810            return have
1811        else
1812            return _codeMember.requirePart.exprs.count > 0
1813
1814   
1815class EnsurePart inherits ContractPart is partial
1816
1817    cue init(codeMember as AbstractMethod)
1818        base.init(codeMember)
1819
1820    cue init(connectToken as IToken?, mainToken as IToken, codeMember as AbstractMethod, exprs as List<of Expr>)
1821        base.init(connectToken, mainToken, codeMember, exprs)
1822        codeMember.ensurePart = this
1823
1824    get connectWord as String is override
1825        return 'and'
1826
1827    def flagVar(varr as LocalVar) is override
1828        varr.isForEnsure = true
1829
1830    get haveConditions as bool is override
1831        if _codeMember.parentBox.hasInvariants
1832            return true
1833        have = false
1834        curCodeMember = _codeMember to ?
1835        post while curCodeMember
1836            if curCodeMember.ensurePart.exprs.count
1837                have = true
1838            curCodeMember = curCodeMember.matchingBaseMember to AbstractMethod?  # TODO: should a cast really be needed here?
1839        return have
1840
1841    def _bindImp
1842        codeMember = _codeMember
1843        if codeMember.resultType is .compiler.voidType
1844            base._bindImp
1845        else
1846            resultLocal = codeMember.findLocal('result')
1847            resultBuiltIn = ResultVar(.token, _codeMember)
1848            if resultLocal, codeMember.replaceLocal(resultBuiltIn)
1849            else, codeMember.addLocal(resultBuiltIn)
1850            try
1851                base._bindImp
1852            finally
1853                if resultLocal, codeMember.replaceLocal(resultLocal to LocalVar)
1854                else, codeMember.removeLocal('result')
Note: See TracBrowser for help on using the browser.