public override string VisitImplDecl(AstImplBlock impl, int data = 0) { var body = string.Join("\n\n", impl.Functions.Select(f => f.Accept(this, 0))); var header = "impl"; // parameters if (impl.Parameters != null) { header += "(" + string.Join(", ", impl.Parameters.Select(p => p.Accept(this, 0))) + ")"; } header += " "; if (impl.TraitExpr != null) { header += impl.TraitExpr.Accept(this) + " for "; } header += impl.TargetTypeExpr.Accept(this); if (impl.Conditions != null) { header += " if " + string.Join(", ", impl.Conditions.Select(c => VisitImplCondition(c))); } return($"{header} {{\n{body.Indent(4)}\n}}"); }
public override NodeFinderResult VisitImplDecl(AstImplBlock impl, int i = 0) { foreach (var s in impl.Functions) { if (GetRelativeLocation(s.Location, i) == RelativeLocation.Same) { return(s.Accept(this, i)); } } return(new NodeFinderResult(impl.Scope, stmt: impl)); }
private void FinishTraitImpl(AstImplBlock impl) { // check if type has required members if (impl.Trait.Declaration.Members.Count > 0) { do { if (!(impl.TargetType is StructType str)) { ReportError(impl.TargetTypeExpr, $"Can't implement trait '{impl.Trait}' for non struct type '{impl.TargetType}' because the trait requires members", ("Trait defined here:", impl.Trait.Declaration.Location)); break; } var strDecl = str.Declaration; ComputeStructMembers(strDecl); foreach (var v in impl.Trait.Declaration.Members) { var member = strDecl.Members.FirstOrDefault(m => m.Name == v.Name); if (member == null) { ReportError(impl.TraitExpr, $"Can't implement trait '{impl.Trait}' for type '{impl.TargetType}' because it misses member '{v.Name}: {v.Type}'", ("Trait member defined here:", v.Location)); continue; } if (!member.IsPublic || member.IsReadOnly) { ReportError(impl.TraitExpr, $"Can't implement trait '{impl.Trait}' for type '{impl.TargetType}' because member '{member.Name}: {member.Type}' is not public", ("Struct member defined here:", member.Location)); continue; } if (member.Type != v.Type) { ReportError(impl.TraitExpr, $"Can't implement trait '{impl.Trait}' for type '{impl.TargetType}' because '{member.Name}' has a different type ({member.Type}) than the trait member ({v.Type})", ("Struct member defined here:", member.Decl.TypeExpr.Location), ("Trait member defined here:", v.Decl.TypeExpr.Location)); continue; } } } while (false); } }
public override string VisitImplDecl(AstImplBlock impl, int data = 0) { var header = "impl"; // parametersu var parameters = impl.IsPolyInstance ? impl.Template.Parameters : impl.Parameters; if (parameters != null) { header += "(" + string.Join(", ", parameters.Select(p => p.Accept(rawPrinter, 0))) + ")"; } header += " "; if (impl.TraitExpr != null) { header += TypeToString(impl.TraitExpr) + " for "; } header += TypeToString(impl.TargetTypeExpr); var conditions = impl.IsPolyInstance ? impl.Template.Conditions : impl.Conditions; if (conditions != null) { header += " if " + string.Join(", ", conditions.Select(c => { switch (c) { case ImplConditionImplTrait t: return($"{TypeToString(t.type)} : {TypeToString(t.trait)}"); case ImplConditionNotYet t: return("#notyet"); case ImplConditionAny a: return(a.Expr.Accept(this)); default: throw new NotImplementedException(); } })); } return(header); }
// helper functions internal void SortDeclarationsIntoImplBlock(AstImplBlock impl) { foreach (var decl in impl.Declarations) { decl.Parent = impl; decl.Scope = impl.SubScope; decl.SetFlag(StmtFlags.ExportScope, impl.GetFlag(StmtFlags.ExportScope)); decl.SourceFile = impl.SourceFile; switch (decl) { case AstConstantDeclaration con: { con.Initializer.AttachTo(con); switch (con.Initializer) { case AstFuncExpr func: impl.Functions.Add(func); break; default: ReportError(con.Initializer, $"This type is not allowed here."); break; } break; } default: ReportError(decl, $"This type of declaration is not allowed here."); break; } } }
public override string VisitImplDecl(AstImplBlock impl, int data = 0) { if (impl.IsPolymorphic) { var body = string.Join("\n\n", impl.Functions.Select(f => f.Accept(this, 0))); var header = "impl"; // parameters if (impl.Parameters != null) { header += "(" + string.Join(", ", impl.Parameters.Select(p => p.Accept(this, 0))) + ")"; } header += " "; if (impl.TraitExpr != null) { header += impl.Trait + " for "; } header += impl.TargetType; if (impl.Conditions != null) { header += " if " + string.Join(", ", impl.Conditions.Select(c => VisitImplCondition(c))); } var sb = new StringBuilder(); sb.Append($"{header} {{\n{body.Indent(4)}\n}}"); // polies if (impl.PolyInstances?.Count > 0) { sb.AppendLine(); sb.AppendLine($"// Polymorphic instances for {header}"); foreach (var pi in impl.PolyInstances) { var args = string.Join(", ", pi.Parameters.Select(p => $"{p.Name.Accept(this)} = {p.Value}")); sb.AppendLine($"// {args}".Indent(4)); sb.AppendLine(pi.Accept(this).Indent(4)); } } return(sb.ToString()); } else { var body = string.Join("\n\n", impl.Functions.Select(f => f.Accept(this, 0))); var header = "impl"; // parameters if (impl.Parameters != null) { header += "(" + string.Join(", ", impl.Parameters.Select(p => p.Accept(this, 0))) + ")"; } header += " "; if (impl.TraitExpr != null) { header += impl.Trait + " for "; } header += impl.TargetType; if (impl.Conditions != null) { header += " if " + string.Join(", ", impl.Conditions.Select(c => VisitImplCondition(c))); } return($"{header} {{\n{body.Indent(4)}\n}}"); } }
private void SetupStructMembers(AstStructTypeExpr expr) { if (expr.Members != null) { return; } expr.Members = new List <AstStructMemberNew>(); if (expr.BaseTrait != null) { // add all members of the trait ComputeTypeMembers(expr.BaseTrait); foreach (var m in expr.BaseTrait.Declaration.Members) { var clone = m.Decl.Clone() as AstVariableDecl; expr.Members.Add(new AstStructMemberNew(clone, true, false, expr.Members.Count)); } // no functions, add impl automatically if (expr.BaseTrait.Declaration.Functions.Count == 0) { expr.Traits.Add(expr.BaseTrait); //AddTraitForType(expr.StructType, // new AstImplBlock( // new List<AstParameter>(), // expr, // expr.TraitExpr, // new List<ImplCondition>(), // new List<AstDecl>(), // expr.TraitExpr)); var impl = new AstImplBlock(null, expr, expr.TraitExpr, null, null, expr.TraitExpr); impl.TargetType = expr.StructType; impl.Trait = expr.BaseTrait; AddTraitForType(expr.StructType, impl); expr.BaseTrait.Declaration.Implementations[expr.StructType] = impl; } } if (expr.TryGetDirective("extend", out var dir)) { if (dir.Arguments.Count != 1) { ReportError(dir, $"Must have one type argument"); } else { var arg = dir.Arguments[0]; arg.AttachTo(expr); arg = ResolveTypeNow(arg, out var type); if (type is StructType str) { if (str.Declaration.Extendable) { expr.Extends = str; } else { ReportError(arg, $"Type '{str}' is not extendable"); } } else if (!type.IsErrorType) { ReportError(arg, $"Argument must be a struct type"); } } } if (expr.Extendable || expr.Extends != null) { var mem = mCompiler.ParseStatement( $"__type_ptr__ : &TypeInfo = @type_info(§self)", new Dictionary <string, AstExpression> { { "self", new AstTypeRef(expr.StructType, expr) } }) as AstVariableDecl; mem.Parent = expr; mem.Scope = expr.SubScope; expr.Members.Add(new AstStructMemberNew(mem, false, true, expr.Members.Count)); } if (expr.Extends != null) { // skip type_info of parent ComputeTypeMembers(expr.Extends); foreach (var m in expr.Extends.Declaration.Members.Skip(1)) { var clone = m.Decl.Clone() as AstVariableDecl; expr.Members.Add(new AstStructMemberNew(clone, true, false, expr.Members.Count)); } } foreach (var decl in expr.Declarations) { if (decl is AstVariableDecl mem) { expr.Members.Add(new AstStructMemberNew(mem, true, false, expr.Members.Count)); } } }
public virtual ReturnType VisitImplDecl(AstImplBlock decl, DataType data = default) => default;
private void Pass3TraitImpl(AstImplBlock impl) { if (!impl.IsPolyInstance) { impl.SubScope = new Scope($"impl {impl.TraitExpr} for {impl.TargetTypeExpr}", impl.Scope); if (impl.Parameters?.Count > 0) { impl.IsPolymorphic = true; } } impl.TraitExpr.Scope = impl.SubScope; if (impl.IsPolymorphic) { // setup scopes foreach (var param in impl.Parameters) { param.Scope = impl.Scope; param.TypeExpr.Scope = impl.Scope; param.TypeExpr.SetFlag(ExprFlags.ValueRequired, true); param.TypeExpr = ResolveTypeNow(param.TypeExpr, out var newType, forceInfer: true); if (newType is AbstractType) { continue; } param.Type = newType; switch (param.Type) { case CheezTypeType _: param.Value = new PolyType(param.Name.Name, true); if (param.Name != null) { impl.SubScope.DefineTypeSymbol(param.Name.Name, new PolyType(param.Name.Name, true)); } break; case IntType _: case FloatType _: case BoolType _: case CharType _: if (param.Name != null) { impl.SubScope.DefineConstant(param.Name.Name, param.Type, new PolyValue(param.Name.Name)); } //impl.SubScope.DefineConstant(param.Name.Name, CheezType.PolyValue, new PolyValue(param.Name.Name)); break; case ErrorType _: break; default: ReportError(param.TypeExpr, $"The type '{param.Type}' is not allowed here."); break; } } } impl.TraitExpr.SetFlag(ExprFlags.ValueRequired, true); impl.TraitExpr = ResolveTypeNow(impl.TraitExpr, out var type, resolvePolyExprToConcreteType: true); if (type is TraitType tt) { impl.Trait = tt; } else { if (!type.IsErrorType) { ReportError(impl.TraitExpr, $"{type} is not a trait"); } impl.Trait = new TraitErrorType(); return; } impl.TargetTypeExpr.SetFlag(ExprFlags.ValueRequired, true); impl.TargetTypeExpr.Scope = impl.SubScope; impl.TargetTypeExpr = ResolveTypeNow(impl.TargetTypeExpr, out var t, resolvePolyExprToConcreteType: !impl.IsPolymorphic); impl.TargetType = t; if (impl.TargetType is StructType @struct) { @struct.Declaration.Traits.Add(impl.Trait); } if (impl.Conditions != null) { if (impl.Parameters == null) { ReportError(new Location(impl.Conditions.First().Location.Beginning, impl.Conditions.Last().Location.End), $"An impl block can't have a condition without parameters"); } foreach (var cond in impl.Conditions) { cond.Scope = impl.SubScope; } } if (impl.IsPolymorphic) { return; } impl.SubScope.DefineTypeSymbol("Self", impl.TargetType); // register impl in trait if (impl.Trait.Declaration.Implementations.TryGetValue(impl.TargetType, out var otherImpl)) { ReportError(impl.TargetTypeExpr, $"There already exists an implementation of trait {impl.Trait} for type {impl.TargetType}", ("Other implementation here:", otherImpl.TargetTypeExpr)); } impl.Trait.Declaration.Implementations[impl.TargetType] = impl; // @TODO: should not be necessary AddTraitForType(impl.TargetType, impl); SortDeclarationsIntoImplBlock(impl); // handle functions foreach (var f in impl.Functions) { f.Scope = impl.SubScope; f.ConstScope = new Scope($"fn$ {f.Name}", f.Scope); f.SubScope = new Scope($"fn {f.Name}", f.ConstScope); f.ImplBlock = impl; InferTypeFuncExpr(f); CheckForSelfParam(f); impl.Scope.DefineImplFunction(f); impl.SubScope.DefineSymbol(f); if (f.Body == null) { ReportError(f.ParameterLocation, $"Function must have an implementation"); } } ComputeTraitMembers(impl.Trait.Declaration); // match functions against trait functions foreach (var traitFunc in impl.Trait.Declaration.Functions) { // find matching function bool found = false; foreach (var func in impl.Functions) { if (func.Name != traitFunc.Name) { continue; } func.TraitFunction = traitFunc; found = true; if (func.SelfType != traitFunc.SelfType) { ReportError(func.ParameterLocation, $"The self parameter of this function doesn't match the trait functions self parameter", ("Trait function defined here:", traitFunc.ParameterLocation)); continue; } if (func.Parameters.Count != traitFunc.Parameters.Count) { ReportError(func.ParameterLocation, $"This function must take the same parameters as the corresponding trait function", ("Trait function defined here:", traitFunc)); continue; } int i = 0; // if first param is self param, skip it if (func.SelfType != SelfParamType.None) { i = 1; } for (; i < func.Parameters.Count; i++) { var fp = func.Parameters[i]; var tp = traitFunc.Parameters[i]; if (tp.Type is SelfType) { if (fp.Type != impl.TargetType) { ReportError( fp.TypeExpr, $"Type of parameter '{fp.Type}' must be the implemented type '{impl.TargetType}'", ("Trait function parameter type defined here:", tp.TypeExpr)); } } else if (!CheezType.TypesMatch(fp.Type, tp.Type)) { ReportError( fp.TypeExpr, $"Type of parameter '{fp.Type}' must match the type of the trait functions parameter '{tp.Type}'", ("Trait function parameter type defined here:", tp.TypeExpr)); } } // check return type if (traitFunc.ReturnType is SelfType) { if (func.ReturnType != impl.TargetType) { ReportError( func.ReturnTypeExpr?.Location ?? func.ParameterLocation, $"Return type '{func.ReturnType}' must be the implemented type '{impl.TargetType}'", ("Trait function parameter type defined here:", traitFunc.ReturnTypeExpr?.Location ?? traitFunc.ParameterLocation)); } } else if (!CheezType.TypesMatch(func.ReturnType, traitFunc.ReturnType)) { ReportError( func.ReturnTypeExpr?.Location ?? func.ParameterLocation, $"Return type must match the trait functions return type", ("Trait function parameter type defined here:", traitFunc.ReturnTypeExpr?.Location ?? traitFunc.ParameterLocation)); } } if (!found) { ReportError( impl.TargetTypeExpr, $"Missing implementation for trait function '{traitFunc.Name}'", ("Trait function defined here:", traitFunc)); } } }
private void Pass3Impl(AstImplBlock impl) { Log($"Pass3Impl {impl.Accept(new SignatureAstPrinter())}", $"poly = {impl.IsPolymorphic}"); PushLogScope(); try { if (!impl.IsPolyInstance) { impl.SubScope = new Scope($"impl {impl.TraitExpr} for {impl.TargetTypeExpr}", impl.Scope); if (impl.Parameters?.Count > 0) { impl.IsPolymorphic = true; } } // check if there are parameters if (impl.IsPolymorphic) { // setup scopes foreach (var param in impl.Parameters) { param.Scope = impl.Scope; param.TypeExpr.Scope = impl.Scope; param.TypeExpr.SetFlag(ExprFlags.ValueRequired, true); param.TypeExpr = ResolveTypeNow(param.TypeExpr, out var newType, forceInfer: true); if (newType is AbstractType) { continue; } param.Type = newType; switch (param.Type) { case CheezTypeType _: param.Value = new PolyType(param.Name.Name, true); if (param.Name != null) { impl.SubScope.DefineTypeSymbol(param.Name.Name, new PolyType(param.Name.Name, true)); } break; case IntType _: case FloatType _: case BoolType _: case CharType _: case EnumType _: if (param.Name != null) { impl.SubScope.DefineConstant(param.Name.Name, param.Type, new PolyValue(param.Name.Name)); } //impl.SubScope.DefineConstant(param.Name.Name, CheezType.PolyValue, new PolyValue(param.Name.Name)); break; case ErrorType _: break; default: ReportError(param.TypeExpr, $"The type '{param.Type}' is not allowed here."); break; } } } impl.TargetTypeExpr.SetFlag(ExprFlags.ValueRequired, true); impl.TargetTypeExpr.Scope = impl.SubScope; impl.TargetTypeExpr = ResolveTypeNow(impl.TargetTypeExpr, out var t, resolvePolyExprToConcreteType: !impl.IsPolymorphic); impl.TargetType = t; // @TODO: does it make sense to allow conditions on impl blocks without parameters? // for now don't allow these if (impl.Conditions != null) { if (impl.Parameters == null) { ReportError(new Location(impl.Conditions.First().Location.Beginning, impl.Conditions.Last().Location.End), $"An impl block can't have a condition without parameters"); } foreach (var cond in impl.Conditions) { cond.Scope = impl.SubScope; } } if (impl.IsPolymorphic) { return; } impl.SubScope.DefineTypeSymbol("Self", impl.TargetType); SortDeclarationsIntoImplBlock(impl); foreach (var f in impl.Functions) { f.Scope = impl.SubScope; f.ConstScope = new Scope($"fn$ {f.Name}", f.Scope); f.SubScope = new Scope($"fn {f.Name}", f.ConstScope); f.ImplBlock = impl; f.SetFlag(ExprFlags.ExportScope, impl.GetFlag(StmtFlags.ExportScope)); InferTypeFuncExpr(f); CheckForSelfParam(f); impl.Scope.DefineImplFunction(f); impl.SubScope.DefineSymbol(f); } } finally { PopLogScope(); Log($"Finished Pass3Impl {impl.Accept(new SignatureAstPrinter())}", $"poly = {impl.IsPolymorphic}"); } }