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); } } }
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; }