Skip to content

Commit 3a9e2c9

Browse files
authored
Merge pull request #1235 from nevkontakte/generics11
Type conversion support between a type parameter and basic types.
2 parents ffff021 + 039acdd commit 3a9e2c9

File tree

6 files changed

+591
-27
lines changed

6 files changed

+591
-27
lines changed

compiler/expressions.go

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,13 +1135,21 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
11351135
}
11361136
}
11371137

1138-
switch t := desiredType.Underlying().(type) {
1138+
_, fromTypeParam := exprType.(*types.TypeParam)
1139+
_, toTypeParam := desiredType.(*types.TypeParam)
1140+
if fromTypeParam || toTypeParam {
1141+
// Conversion from or to a type param can only be done at runtime, since the
1142+
// concrete type is not known to the compiler at compile time.
1143+
return fc.formatExpr("%s.convertFrom(%s.wrap(%e))", fc.typeName(desiredType), fc.typeName(exprType), expr)
1144+
}
1145+
1146+
switch dst := desiredType.Underlying().(type) {
11391147
case *types.Basic:
11401148
switch {
1141-
case isInteger(t):
1149+
case isInteger(dst):
11421150
basicExprType := exprType.Underlying().(*types.Basic)
11431151
switch {
1144-
case is64Bit(t):
1152+
case is64Bit(dst):
11451153
if !is64Bit(basicExprType) {
11461154
if basicExprType.Kind() == types.Uintptr { // this might be an Object returned from reflect.Value.Pointer()
11471155
return fc.formatExpr("new %1s(0, %2e.constructor === Number ? %2e : 1)", fc.typeName(desiredType), expr)
@@ -1150,25 +1158,25 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
11501158
}
11511159
return fc.formatExpr("new %1s(%2h, %2l)", fc.typeName(desiredType), expr)
11521160
case is64Bit(basicExprType):
1153-
if !isUnsigned(t) && !isUnsigned(basicExprType) {
1154-
return fc.fixNumber(fc.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), t)
1161+
if !isUnsigned(dst) && !isUnsigned(basicExprType) {
1162+
return fc.fixNumber(fc.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), dst)
11551163
}
1156-
return fc.fixNumber(fc.formatExpr("%s.$low", fc.translateExpr(expr)), t)
1164+
return fc.fixNumber(fc.formatExpr("%s.$low", fc.translateExpr(expr)), dst)
11571165
case isFloat(basicExprType):
11581166
return fc.formatParenExpr("%e >> 0", expr)
11591167
case types.Identical(exprType, types.Typ[types.UnsafePointer]):
11601168
return fc.translateExpr(expr)
11611169
default:
1162-
return fc.fixNumber(fc.translateExpr(expr), t)
1170+
return fc.fixNumber(fc.translateExpr(expr), dst)
11631171
}
1164-
case isFloat(t):
1165-
if t.Kind() == types.Float32 && exprType.Underlying().(*types.Basic).Kind() == types.Float64 {
1172+
case isFloat(dst):
1173+
if dst.Kind() == types.Float32 && exprType.Underlying().(*types.Basic).Kind() == types.Float64 {
11661174
return fc.formatExpr("$fround(%e)", expr)
11671175
}
11681176
return fc.formatExpr("%f", expr)
1169-
case isComplex(t):
1177+
case isComplex(dst):
11701178
return fc.formatExpr("new %1s(%2r, %2i)", fc.typeName(desiredType), expr)
1171-
case isString(t):
1179+
case isString(dst):
11721180
value := fc.translateExpr(expr)
11731181
switch et := exprType.Underlying().(type) {
11741182
case *types.Basic:
@@ -1187,7 +1195,7 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
11871195
default:
11881196
panic(fmt.Sprintf("Unhandled conversion: %v\n", et))
11891197
}
1190-
case t.Kind() == types.UnsafePointer:
1198+
case dst.Kind() == types.UnsafePointer:
11911199
if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND {
11921200
if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr {
11931201
return fc.formatExpr("$sliceToNativeArray(%s)", fc.translateConversionToSlice(indexExpr.X, types.NewSlice(types.Typ[types.Uint8])))
@@ -1218,7 +1226,7 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
12181226
switch et := exprType.Underlying().(type) {
12191227
case *types.Basic:
12201228
if isString(et) {
1221-
if types.Identical(t.Elem().Underlying(), types.Typ[types.Rune]) {
1229+
if types.Identical(dst.Elem().Underlying(), types.Typ[types.Rune]) {
12221230
return fc.formatExpr("new %s($stringToRunes(%e))", fc.typeName(desiredType), expr)
12231231
}
12241232
return fc.formatExpr("new %s($stringToBytes(%e))", fc.typeName(desiredType), expr)
@@ -1234,7 +1242,7 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
12341242
break
12351243
}
12361244

1237-
switch ptrElType := t.Elem().Underlying().(type) {
1245+
switch ptrElType := dst.Elem().Underlying().(type) {
12381246
case *types.Array: // (*[N]T)(expr) — converting expr to a pointer to an array.
12391247
if _, ok := exprType.Underlying().(*types.Slice); ok {
12401248
return fc.formatExpr("$sliceToGoArray(%e, %s)", expr, fc.typeName(desiredType))
@@ -1249,7 +1257,7 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
12491257
// indeed pointing at a byte array.
12501258
array := fc.newLocalVariable("_array")
12511259
target := fc.newLocalVariable("_struct")
1252-
return fc.formatExpr("(%s = %e, %s = %e, %s, %s)", array, expr, target, fc.zeroValue(t.Elem()), fc.loadStruct(array, target, ptrElType), target)
1260+
return fc.formatExpr("(%s = %e, %s = %e, %s, %s)", array, expr, target, fc.zeroValue(dst.Elem()), fc.loadStruct(array, target, ptrElType), target)
12531261
}
12541262
// Convert between structs of different types but identical layouts,
12551263
// for example:
@@ -1271,8 +1279,8 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
12711279
// TODO(nevkontakte): Are there any other cases that fall into this case?
12721280
exprTypeElem := exprType.Underlying().(*types.Pointer).Elem()
12731281
ptrVar := fc.newLocalVariable("_ptr")
1274-
getterConv := fc.translateConversion(fc.setType(&ast.StarExpr{X: fc.newIdent(ptrVar, exprType)}, exprTypeElem), t.Elem())
1275-
setterConv := fc.translateConversion(fc.newIdent("$v", t.Elem()), exprTypeElem)
1282+
getterConv := fc.translateConversion(fc.setType(&ast.StarExpr{X: fc.newIdent(ptrVar, exprType)}, exprTypeElem), dst.Elem())
1283+
setterConv := fc.translateConversion(fc.newIdent("$v", dst.Elem()), exprTypeElem)
12761284
return fc.formatExpr("(%1s = %2e, new %3s(function() { return %4s; }, function($v) { %1s.$set(%5s); }, %1s.$target))", ptrVar, expr, fc.typeName(desiredType), getterConv, setterConv)
12771285

12781286
case *types.Interface:

compiler/prelude/jsmapping.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var $jsObjectPtr, $jsErrorPtr;
1+
var $jsObjectPtr, $jsErrorPtr; // Initialized by init() in the runtime package.
22

33
var $needsExternalization = t => {
44
switch (t.kind) {

0 commit comments

Comments
 (0)