コード例 #1
0
        public override void Validate(ComputationContext ctx)
        {
            base.Validate(ctx);

            if (this.Modifier.HasTrait)
            {
                if (!this.Name.Parameters.Any())
                {
                    ctx.AddError(ErrorCode.NonGenericTrait, this);
                }
                else
                {
                    if (!this.Constraints.Any())
                    {
                        ctx.AddError(ErrorCode.UnconstrainedTrait, this);
                    }

                    TypeDefinition host_type = AssociatedHost;
                    if (host_type == null)
                    {
                        ctx.AddError(ErrorCode.MissingHostTypeForTrait, this);
                    }
                }
            }

            if (this.Modifier.HasAssociatedReference)
            {
                if (!this.Modifier.IsSealed)
                {
                    ctx.AddError(ErrorCode.AssociatedReferenceRequiresSealedType, this.Modifier);
                }

                {
                    IEnumerable <FunctionDefinition> constructors = this.NestedFunctions.Where(it => it.IsInitConstructor());

                    foreach (FunctionDefinition cons in constructors.Skip(1))
                    {
                        ctx.AddError(ErrorCode.AssociatedReferenceRequiresSingleConstructor, cons);
                    }

                    FunctionDefinition primary_cons = constructors.First();
                    if (primary_cons.Parameters.Count != 1)
                    {
                        ctx.AddError(ErrorCode.AssociatedReferenceRequiresSingleParameter, primary_cons);
                    }
                    else
                    {
                        FunctionParameter cons_param = primary_cons.Parameters.Single();
                        if (!ctx.Env.IsReferenceOfType(cons_param.TypeName.Evaluation.Components))
                        {
                            ctx.AddError(ErrorCode.AssociatedReferenceRequiresReferenceParameter, cons_param.TypeName);
                        }
                        else if (cons_param.IsVariadic)
                        {
                            ctx.AddError(ErrorCode.AssociatedReferenceRequiresNonVariadicParameter, cons_param);
                        }
                        else if (cons_param.IsOptional)
                        {
                            ctx.AddError(ErrorCode.AssociatedReferenceRequiresNonOptionalParameter, cons_param);
                        }
                    }
                }

                {
                    IEnumerable <VariableDeclaration> ref_fields = this.NestedFields
                                                                   .Where(it => ctx.Env.IsReferenceOfType(it.Evaluation.Components));

                    foreach (VariableDeclaration decl in ref_fields.Skip(1))
                    {
                        ctx.AddError(ErrorCode.AssociatedReferenceRequiresSingleReferenceField, decl);
                    }
                }
            }

            {
                IEnumerable <VariableDeclaration> ref_fields = this.NestedFields
                                                               .Where(it => ctx.Env.IsReferenceOfType(it.Evaluation.Components));

                VariableDeclaration primary = ref_fields.FirstOrDefault();
                if (primary != null && primary.IsReassignable(ctx))
                {
                    ctx.AddError(ErrorCode.ReferenceFieldCannotBeReassignable, primary);
                }
            }

            foreach (NameReference parent in this.ParentNames.Skip(1))
            {
                if (parent.Evaluation.Components.Cast <EntityInstance>().TargetType.IsTypeImplementation)
                {
                    ctx.AddError(ErrorCode.TypeImplementationAsSecondaryParent, parent);
                }
            }

            {
                TypeDefinition primary_parent = this.Inheritance.GetTypeImplementationParent()?.TargetType;
                if (primary_parent != null)
                {
                    if (this.Modifier.HasEnum != primary_parent.Modifier.HasEnum)
                    {
                        ctx.AddError(ErrorCode.EnumCrossInheritance, this);
                    }

                    if (this.Modifier.HasTrait)
                    {
                        ctx.AddError(ErrorCode.TraitInheritingTypeImplementation, this.ParentNames.First());
                    }
                }
            }

            if (!this.Modifier.IsAbstract)
            {
                foreach (FunctionDefinition func in this.AllNestedFunctions)
                {
                    if (func.Modifier.HasAbstract)
                    {
                        ctx.AddError(ErrorCode.NonAbstractTypeWithAbstractMethod, func);
                    }
                    else if (func.Modifier.HasBase && this.Modifier.IsSealed)
                    {
                        ctx.AddError(ErrorCode.SealedTypeWithBaseMethod, func);
                    }
                }
            }

            {
                TypeMutability current_mutability = this.InstanceOf.MutabilityOfType(ctx);
                if (current_mutability != TypeMutability.ForceMutable)
                {
                    // the above check is more than checking just a flag
                    // for template types the mutability depends on parameter constraints
                    foreach (NameReference parent in this.ParentNames)
                    {
                        TypeMutability parent_mutability = parent.Evaluation.Components.MutabilityOfType(ctx);
                        if (parent_mutability == TypeMutability.ForceMutable)
                        {
                            ctx.AddError(ErrorCode.InheritanceMutabilityViolation, parent);
                        }
                    }
                }
            }

            if (!this.Modifier.HasMutable)
            {
                foreach (VariableDeclaration field in this.AllNestedFields)
                {
                    if (field.Modifier.HasReassignable)
                    {
                        ctx.AddError(ErrorCode.MutableFieldInImmutableType, field);
                    }
                    else
                    {
                        TypeMutability field_eval_mutability = field.Evaluation.Components.MutabilityOfType(ctx);
                        if (!field_eval_mutability.HasFlag(TypeMutability.ConstAsSource) &&
                            !field_eval_mutability.HasFlag(TypeMutability.GenericUnknownMutability) &&
                            !field_eval_mutability.HasFlag(TypeMutability.ForceConst))
                        {
                            ctx.AddError(ErrorCode.MutableFieldInImmutableType, field);
                        }
                    }
                }
                foreach (FunctionDefinition func in this.NestedFunctions
                         .Where(it => it.Modifier.HasMutable))
                {
                    ctx.AddError(ErrorCode.MutableFunctionInImmutableType, func);
                }

                foreach (Property property in this.NestedProperties
                         .Where(it => it.Setter != null))
                {
                    ctx.AddError(ErrorCode.PropertySetterInImmutableType, property);
                }
            }
        }
コード例 #2
0
        private void compute(ComputationContext ctx)
        {
            foreach (TypeDefinition trait in this.AssociatedTraits)
            {
                trait.computeAncestors(ctx, new HashSet <TypeDefinition>());
            }
            computeAncestors(ctx, new HashSet <TypeDefinition>());

            IEnumerable <INode> owned_nodes = this.ChildrenNodes.Concat(this.AssociatedTraits.SelectMany(it => it.ChildrenNodes));

            owned_nodes.WhereType <ISurfable>().ForEach(it => it.Surfed(ctx));

            // --

            if (this.Modifier.HasMutable &&
                (!this.Modifier.HasHeapOnly || this.NestedFunctions.Any(it => it.IsCopyInitConstructor(ctx))))
            {
                // creating counterparts of mutable methods

                // todo: this should be implemented differently -- instead of creating methods, creating expressions
                // copy-cons, mutable call, return obj
                // on demand, in-place, when const non-existing method is called

                HashSet <string> const_functions = this.NestedFunctions.Where(f => !f.Modifier.HasMutable).Concat(
                    this.Inheritance.OrderedAncestorsIncludingObject.SelectMany(it => it.TargetType.NestedFunctions
                                                                                .Where(f => !f.Modifier.HasMutable && !f.Modifier.HasPrivate)))
                                                   .Select(it => it.Name.Name)
                                                   .ToHashSet();

                foreach (FunctionDefinition func in this.NestedFunctions.Where(it => it.Modifier.HasMutable).StoreReadOnly())
                {
                    string const_name = NameFactory.UnmutableName(func.Name.Name);
                    if (const_functions.Contains(const_name))
                    {
                        continue;
                    }

                    if (!ctx.Env.IsOfUnitType(func.ResultTypeName))
                    {
                        continue;
                    }

                    INameReference result_typename = NameFactory.ItNameReference();
                    if (this.Modifier.HasHeapOnly)
                    {
                        result_typename = NameFactory.PointerNameReference(result_typename);
                    }

                    var instructions = new List <IExpression>();
                    if (this.Modifier.HasHeapOnly)
                    {
                        instructions.Add(VariableDeclaration.CreateStatement("cp", null,
                                                                             ExpressionFactory.HeapConstructor(NameFactory.ItNameReference(), NameReference.CreateThised())));
                    }
                    else
                    {
                        // explicit typename is needed, because otherwise we would get a reference to this, not value (copy)
                        instructions.Add(VariableDeclaration.CreateStatement("cp", NameFactory.ItNameReference(),
                                                                             NameReference.CreateThised()));
                    }

                    instructions.Add(FunctionCall.Create(NameReference.Create("cp", func.Name.Name),
                                                         func.Parameters.Select(it => NameReference.Create(it.Name.Name)).ToArray()));
                    instructions.Add(Return.Create(NameReference.Create("cp")));

                    FunctionDefinition const_func = FunctionBuilder.Create(const_name, func.Name.Parameters,
                                                                           result_typename,
                                                                           Block.CreateStatement(instructions))
                                                    .SetModifier(EntityModifier.AutoGenerated)
                                                    // native methods have parameters with forbidden read
                                                    .Parameters(func.Parameters.Select(it => it.CloneAsReadable()));

                    this.AddNode(const_func);
                    const_func.Surfed(ctx);
                }
            }



            if (this.Modifier.HasEnum)
            {
                // finally we are at point when we know from which offset we can start setting ids for enum cases

                FunctionDefinition zero_constructor = this.NestedFunctions.Single(it => it.IsZeroConstructor() && it.Modifier.HasStatic);
                if (zero_constructor.IsComputed)
                {
                    throw new System.Exception("Internal error -- we cannot alter the body after the function was already computed");
                }

                int enum_ord = this.InstanceOf.PrimaryAncestors(ctx).Select(it => it.TargetType)
                               .Sum(it => it.NestedFields.Count(f => f.Modifier.HasEnum));

                foreach (VariableDeclaration decl in this.NestedFields.Where(it => it.Modifier.HasEnum))
                {
                    zero_constructor.UserBody.Append(decl.CreateFieldInitCall(NatLiteral.Create($"{enum_ord++}")));
                }
            }

            // base method -> derived (here) method
            var virtual_mapping = new VirtualTable(isPartial: false);

            foreach (EntityInstance parent_instance in this.Inheritance.MinimalParentsIncludingObject
                     .Concat(this.AssociatedTraits.SelectMany(it => it.Inheritance.MinimalParentsIncludingObject))
                     .Distinct(EntityInstance.Comparer)
                     .Reverse())
            {
                virtual_mapping.OverrideWith(parent_instance.TargetType.InheritanceVirtualTable);
            }

            IEnumerable <FunctionDefinition> all_nested_functions = this.AllNestedFunctions
                                                                    .Concat(this.AssociatedTraits.SelectMany(it => it.AllNestedFunctions));

            // derived (here) method -> base methods
            Dictionary <FunctionDefinition, List <FunctionDefinition> > derivation_mapping = all_nested_functions
                                                                                             .Where(it => it.Modifier.HasOverride)
                                                                                             .ToDictionary(it => it, it => new List <FunctionDefinition>());

            var inherited_member_instances = new HashSet <EntityInstance>(EntityInstance.Comparer);

            var missing_func_implementations = new List <FunctionDefinition>();

            foreach (EntityInstance ancestor in this.Inheritance.OrderedAncestorsIncludingObject
                     .Concat(this.AssociatedTraits.SelectMany(it => it.Inheritance.OrderedAncestorsIncludingObject))
                     .Distinct(EntityInstance.Comparer))
            {
                // special case are properties -- properties are in fact not inherited, their accessors are
                // however user sees properties, so we get members here (including properties), but when we compute
                // what function overrode which, we use really functions, and only having them in hand we check if
                // they are property accessors
                IEnumerable <EntityInstance> members = (ancestor.TargetType.AvailableEntities ?? Enumerable.Empty <EntityInstance>())
                                                       .Where(it => it.Target is IMember);

                foreach (EntityInstance entity_instance in members
                         .Where(it => it.Target.Modifier.HasPublic || it.Target.Modifier.HasProtected)
                         .Where(it => !(it.Target is FunctionDefinition func) || !func.IsAnyConstructor()))
                {
                    EntityInstance translated = entity_instance.TranslateThrough(ancestor);
                    inherited_member_instances.Add(translated);
                }

                foreach (FunctionDerivation deriv_info in TypeDefinitionExtension.PairDerivations(ctx, ancestor, all_nested_functions))
                {
                    if (deriv_info.Derived == null)
                    {
                        // we can skip implementation or abstract signature of the function in the abstract type
                        if (deriv_info.Base.Modifier.RequiresOverride && !this.Modifier.IsAbstract)
                        {
                            missing_func_implementations.Add(deriv_info.Base);
                        }
                    }
                    else
                    {
                        {
                            if (deriv_info.Base.IsPropertyAccessor(out Property base_property))
                            {
                                EntityInstance base_prop_instance = base_property.InstanceOf.TranslateThrough(ancestor);
                                inherited_member_instances.Remove(base_prop_instance);
                            }

                            EntityInstance base_instance = deriv_info.Base.InstanceOf.TranslateThrough(ancestor);
                            inherited_member_instances.Remove(base_instance);
                        }

                        if (deriv_info.Derived.Modifier.HasOverride)
                        {
                            derivation_mapping[deriv_info.Derived].Add(deriv_info.Base);
                            // user does not have to repeat "pinned" all the time, but for easier tracking of pinned methods
                            // we add it automatically
                            if (deriv_info.Base.Modifier.HasPinned)
                            {
                                deriv_info.Derived.SetModifier(deriv_info.Derived.Modifier | EntityModifier.Pinned);
                            }

                            if (deriv_info.Base.Modifier.HasHeapOnly != deriv_info.Derived.Modifier.HasHeapOnly)
                            {
                                ctx.AddError(ErrorCode.HeapRequirementChangedOnOverride, deriv_info.Derived);
                            }
                        }
                        else if (!deriv_info.Base.Modifier.IsSealed)
                        {
                            ctx.AddError(ErrorCode.MissingOverrideModifier, deriv_info.Derived);
                        }

                        if (!deriv_info.Base.Modifier.IsSealed)
                        {
                            virtual_mapping.Update(deriv_info.Base, deriv_info.Derived);

                            // the rationale for keeping the same access level is this
                            // narrowing is pretty easy to skip by downcasting
                            // expanding looks like a good idea, but... -- the author of original type
                            // maybe had better understanding why given function is private/protected and not protected/public
                            // it is better to keep it safe, despite little annoyance (additional wrapper), than finding out
                            // how painful is violating that "good reason" because it was too easy to type "public"
                            if (!deriv_info.Base.Modifier.SameAccess(deriv_info.Derived.Modifier))
                            {
                                ctx.AddError(ErrorCode.AlteredAccessLevel, deriv_info.Derived.Modifier);
                            }
                        }
                        else if (deriv_info.Derived.Modifier.HasOverride)
                        {
                            ctx.AddError(ErrorCode.CannotOverrideSealedMethod, deriv_info.Derived);
                        }
                    }
                }
            }

            foreach (FunctionDefinition missing_impl in missing_func_implementations)
            {
                if (!isDerivedByAncestors(missing_impl))
                {
                    ctx.AddError(ErrorCode.BaseFunctionMissingImplementation, this, missing_impl);
                }
            }

            // here we eliminate "duplicate" entries -- if we have some method in ancestor A
            // and this method is overridden in ancestor B, then we would like to have
            // only method B listed, not both
            foreach (EntityInstance inherited in inherited_member_instances.ToArray())
            {
                IEntity target = inherited.Target;
                if (target is Property prop)
                {
                    target = prop.Getter;
                }
                if (target is FunctionDefinition func && isDerivedByAncestors(func))
                {
                    inherited_member_instances.Remove(inherited);
                }
            }

            this.InheritanceVirtualTable = virtual_mapping;
            this.DerivationTable         = new DerivationTable(ctx, this, derivation_mapping);
            this.availableEntities       = ScopeTable.Combine(this.AvailableEntities, inherited_member_instances);

            foreach (FunctionDefinition func in derivation_mapping.Where(it => !it.Value.Any()).Select(it => it.Key))
            {
                ctx.AddError(ErrorCode.NothingToOverride, func);
            }

            this.isEvaluated = true;
        }