From 75c3f42594d60b0a026ec175dd0b2d381ddbd11f Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Mon, 2 Nov 2015 13:43:15 -0800 Subject: [PATCH] Generate proper C++ for capture by-reference. --- ast/jsonInterfaces.ts | 3 +++ embedded/emitter.ts | 25 ++++++++++++++++++++----- embedded/helpers.ts | 20 +++++++++++++++++++- embedded/visitor.ts | 5 +++-- rt/cloud.ts | 2 +- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/ast/jsonInterfaces.ts b/ast/jsonInterfaces.ts index ba7de0d5..670f8497 100644 --- a/ast/jsonInterfaces.ts +++ b/ast/jsonInterfaces.ts @@ -250,6 +250,8 @@ module TDev.AST.Json locals?:JLocalDef[]; // this contains the reference in short mode; it never contains anything else isImplicit:boolean; isOptional:boolean; + allLocals: JLocalDef[]; + capturedLocals: JLocalDef[]; } export interface JOptionalParameter extends JNode @@ -377,6 +379,7 @@ module TDev.AST.Json { name:string; type:JTypeRef; + isByRef: boolean; } // Response to: diff --git a/embedded/emitter.ts b/embedded/emitter.ts index de1ae022..81672aed 100644 --- a/embedded/emitter.ts +++ b/embedded/emitter.ts @@ -88,8 +88,11 @@ module TDev { public visitExprHolder(env: H.Env, locals: J.JLocalDef[], expr: J.JExprHolder) { var decls = locals.map(d => { - var x = H.defaultValueForType(this.libraryMap, d.type); - return this.visit(env, d) + (x ? " = " + x : "") + ";"; + // Side-effect: marks [d] as promoted, if needed. + var decl = this.visit(env, d); + var defaultValue = H.defaultValueForType(this.libraryMap, d.type); + var initialValue = !H.isPromoted(env, d.id) && defaultValue ? " = " + defaultValue : ""; + return decl + initialValue + ";"; }); return decls.join("\n"+env.indent) + (decls.length ? "\n" + env.indent : "") + @@ -104,11 +107,23 @@ module TDev { // which C and C++ accept both "f" and "&f" (we hence use the former) // - arrays, strings, user-defined objects, which are in fact of type // "ManagedType", no "&" operator here. - return H.resolveLocal(env, name, id); + // However, we now support capture-by-reference. This means that the + // data is ref-counted (so as to be shared), but assignment and + // reference operate on the ref-counted data (not on the pointer), so we + // must add a dereference there. + var prefix = H.isPromoted(env, id) ? "*" : ""; + return prefix+H.resolveLocal(env, name, id); } - public visitLocalDef(env: H.Env, name: string, id: string, type: J.JTypeRef) { - return H.mkType(env, this.libraryMap, type)+" "+H.resolveLocal(env, name, id); + public visitLocalDef(env: H.Env, name: string, id: string, type: J.JTypeRef, isByRef: boolean) { + var t = H.mkType(env, this.libraryMap, type); + var l = H.resolveLocal(env, name, id); + if (H.shouldPromoteToRef(env, type, isByRef)) { + H.markPromoted(env, id); + return "ManagedType<"+ t + "> " + l + "(new "+t+")"; + } else { + return t + " " + l; + } } // Allows the target to redefine their own string type. diff --git a/embedded/helpers.ts b/embedded/helpers.ts index c70d0c76..f71483e6 100644 --- a/embedded/helpers.ts +++ b/embedded/helpers.ts @@ -140,6 +140,10 @@ module TDev { libName: string; indent: string; + + // A table of id's that have been promoted to ref-counted types (because + // they were captured by a closure). Modified in an imperative manner. + promotedIds: StringMap; } export function indent(e: Env): Env { @@ -149,9 +153,18 @@ module TDev { globalNameMap: e.globalNameMap, libName: e.libName, indent: e.indent + " ", + promotedIds: e.promotedIds, }; } + export function markPromoted(e: Env, id: string) { + e.promotedIds[id] = true; + } + + export function isPromoted(e: Env, id: string) { + return e.promotedIds[id]; + } + export function emptyEnv(g: GlobalNameMap, libName: string): Env { var usedNames: StringMap = {}; var m = libName ? g.libraries[libName] : g.program; @@ -165,6 +178,7 @@ module TDev { globalNameMap: g, libName: libName, indent: "", + promotedIds: {}, }; } @@ -240,6 +254,10 @@ module TDev { return n; } + export function shouldPromoteToRef(e: Env, t: J.JTypeRef, isByRef: boolean) { + return typeof t == "string" && isByRef; + } + // --- Helper functions. // For constructing / modifying AST nodes. @@ -325,7 +343,7 @@ module TDev { var retType = outParams.length ? mkType(env, libMap, outParams[0].type) : "void"; var args = "(" + inParams.map(p => mkParam(env, libMap, p)).join(", ") + ")"; if (isLambda) - return "[=] "+args+" -> "+retType; + return "[=] "+args+" mutable -> "+retType; else return retType + " " + name + args; } diff --git a/embedded/visitor.ts b/embedded/visitor.ts index 58570795..2b73b520 100644 --- a/embedded/visitor.ts +++ b/embedded/visitor.ts @@ -30,7 +30,7 @@ module TDev { return this.visitGlobalDef(env, n13.name, n13.type, n13.comment); case "localDef": var n1 = n; - return this.visitLocalDef(env, n1.name, n1.id, n1.type); + return this.visitLocalDef(env, n1.name, n1.id, n1.type, n1.isByRef); case "localRef": var n11 = n; return this.visitLocalRef(env, n11.name, n11.localId); @@ -115,7 +115,8 @@ module TDev { env: T, name: string, id: string, - type: J.JTypeRef): U { throw new Error("Not implemented"); } + type: J.JTypeRef, + isByRef: boolean): U { throw new Error("Not implemented"); } public visitLocalRef(env: T, name: string, id: string): U { throw new Error("Not implemented"); } public visitExprHolder( env: T, diff --git a/rt/cloud.ts b/rt/cloud.ts index ae5de7e9..5f0dcd2d 100644 --- a/rt/cloud.ts +++ b/rt/cloud.ts @@ -4,7 +4,7 @@ module TDev.Cloud { export var lite = false; export var fullTD = true; export var litePermissions:StringMap = {}; - var microbitGitTag = "v12"; + var microbitGitTag = "v13"; export var useEmbeddedGcc = true; export var useNativeCompilation = false;