--- /dev/null
+// CS0029: Cannot implicitly convert type `void' to `object'
+// Line: 10
+
+using System;
+
+class TestClass
+{
+ public static void Main ()
+ {
+ Console.WriteLine ($"{ Main () }");
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS0150: A constant value is expected
+// Line: 13
+
+class Program
+{
+ static int Arg ()
+ {
+ return 4;
+ }
+
+ static void Main()
+ {
+ var s = $"{1,Arg()}";
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS0266: Cannot implicitly convert type `double' to `int'. An explicit conversion exists (are you missing a cast?)
+// Line: 8
+
+class Program
+{
+ static void Main()
+ {
+ var s = $"{1, 0.0}";
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8094: Alignment value has a magnitude greater than 32767 and may result in a large formatted string
+// Line: 9
+// Compiler options: -warnaserror
+
+class Program
+{
+ static void Main ()
+ {
+ var s = $"{1, int.MinValue }";
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8094: Alignment value has a magnitude greater than 32767 and may result in a large formatted string
+// Line: 9
+// Compiler options: -warnaserror
+
+class Program
+{
+ static void Main ()
+ {
+ var s = $"{1, int.MaxValue }";
+ }
+}
\ No newline at end of file
public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl)
{
- if (!expl && IsLiteral &&
+ if (!expl && IsLiteral && type.BuiltinType != BuiltinTypeSpec.Type.Double &&
BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (target) &&
BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (type)) {
ec.Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'",
if (expr_type.IsStruct && TypeSpecComparer.IsEqual (expr_type, target_type))
return expr_type == target_type ? expr : EmptyCast.Create (expr, target_type);
+ var interpolated_string = expr as InterpolatedString;
+ if (interpolated_string != null) {
+ if (target_type == ec.Module.PredefinedTypes.IFormattable.TypeSpec || target_type == ec.Module.PredefinedTypes.FormattableString.TypeSpec)
+ return interpolated_string.ConvertTo (ec, target_type);
+ }
+
return null;
}
{
$$ = new InterpolatedStringInsert ((Expression) $1);
}
- | expression COMMA LITERAL
+ | expression COMMA expression
{
$$ = new InterpolatedStringInsert ((Expression) $1) {
- Alignment = (Constant)$3
+ Alignment = (Expression)$3
};
}
| expression COLON
Format = (string)$4
};
}
- | expression COMMA LITERAL COLON
+ | expression COMMA expression COLON
{
lexer.parsing_interpolation_format = true;
}
lexer.parsing_interpolation_format = false;
$$ = new InterpolatedStringInsert ((Expression) $1) {
- Alignment = (Constant)$3,
+ Alignment = (Expression)$3,
Format = (string) $6
};
}
{
readonly StringLiteral start, end;
readonly List<Expression> interpolations;
+ Arguments arguments;
public InterpolatedString (StringLiteral start, List<Expression> interpolations, StringLiteral end)
{
this.start = start;
this.end = end;
this.interpolations = interpolations;
+ loc = start.Location;
}
- public override Expression CreateExpressionTree (ResolveContext ec)
+ public Expression ConvertTo (ResolveContext rc, TypeSpec type)
{
- throw new NotSupportedException ();
+ var factory = rc.Module.PredefinedTypes.FormattableStringFactory.Resolve ();
+ if (factory == null)
+ return null;
+
+ var ma = new MemberAccess (new TypeExpression (factory, loc), "Create", loc);
+ var res = new Invocation (ma, arguments).Resolve (rc);
+ if (res != null && res.Type != type)
+ res = Convert.ExplicitConversion (rc, res, type, loc);
+
+ return res;
+ }
+
+ public override bool ContainsEmitWithAwait ()
+ {
+ if (interpolations == null)
+ return false;
+
+ foreach (var expr in interpolations) {
+ if (expr.ContainsEmitWithAwait ())
+ return true;
+ }
+
+ return false;
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext rc)
+ {
+ var best = ResolveBestFormatOverload (rc);
+ if (best == null)
+ return null;
+
+ Expression instance = new NullLiteral (loc);
+ var args = Arguments.CreateForExpressionTree (rc, arguments, instance, new TypeOfMethod (best, loc));
+ return CreateExpressionFactoryCall (rc, "Call", args);
}
protected override Expression DoResolve (ResolveContext rc)
{
- if (interpolations != null) {
+ string str;
+
+ if (interpolations == null) {
+ str = start.Value;
+ arguments = new Arguments (1);
+ } else {
for (int i = 0; i < interpolations.Count; i += 2) {
var ipi = (InterpolatedStringInsert)interpolations [i];
ipi.Resolve (rc);
}
- }
- string str;
- Arguments arguments;
- if (interpolations != null) {
arguments = new Arguments (interpolations.Count);
var sb = new StringBuilder (start.Value);
var isi = (InterpolatedStringInsert)interpolations [i];
if (isi.Alignment != null) {
sb.Append (',');
- sb.Append (isi.Alignment.GetValueAsLong ());
+ var value = isi.ResolveAligment (rc);
+ if (value != null)
+ sb.Append (value.Value);
}
if (isi.Format != null) {
sb.Append (end.Value);
str = sb.ToString ();
- } else {
- arguments = new Arguments (1);
- str = start.Value;
}
arguments.Insert (0, new Argument (new StringLiteral (rc.BuiltinTypes, str, start.Location)));
- var members = MemberCache.FindMembers (rc.BuiltinTypes.String, "Format", true);
- var res = new OverloadResolver (members, OverloadResolver.Restrictions.NoBaseMembers, loc);
- var best = res.ResolveMember<MethodSpec> (rc, ref arguments);
+ eclass = ExprClass.Value;
+ type = rc.BuiltinTypes.String;
+ return this;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ // No interpolation, convert to simple string result (needs to match string.Format unescaping)
+ if (interpolations == null) {
+ var str = start.Value.Replace ("{{", "{").Replace ("}}", "}");
+ if (str != start.Value)
+ new StringConstant (ec.BuiltinTypes, str, loc).Emit (ec);
+ else
+ start.Emit (ec);
+
+ return;
+ }
+
+ var best = ResolveBestFormatOverload (new ResolveContext (ec.MemberContext));
if (best == null)
- return null;
+ return;
- return new Invocation (MethodGroupExpr.CreatePredefined (best, best.DeclaringType, Location.Null), arguments).Resolve (rc);
+ var ca = new CallEmitter ();
+ ca.Emit (ec, best, arguments, loc);
}
- public override void Emit (EmitContext ec)
+ MethodSpec ResolveBestFormatOverload (ResolveContext rc)
{
- throw new NotSupportedException ();
+ var members = MemberCache.FindMembers (rc.BuiltinTypes.String, "Format", true);
+ var res = new OverloadResolver (members, OverloadResolver.Restrictions.NoBaseMembers, loc);
+ return res.ResolveMember<MethodSpec> (rc, ref arguments);
}
}
{
}
- public Constant Alignment { get; set; }
+ public Expression Alignment { get; set; }
public string Format { get; set; }
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ var expr = base.DoResolve (rc);
+ if (expr == null)
+ return null;
+
+ //
+ // For better error reporting, assumes the built-in implementation uses object
+ // as argument(s)
+ //
+ return Convert.ImplicitConversionRequired (rc, expr, rc.BuiltinTypes.Object, expr.Location);
+ }
+
+ public int? ResolveAligment (ResolveContext rc)
+ {
+ var c = Alignment.ResolveLabelConstant (rc);
+ if (c == null)
+ return null;
+
+ c = c.ImplicitConversionRequired (rc, rc.BuiltinTypes.Int);
+ if (c == null)
+ return null;
+
+ var value = (int) c.GetValueAsLong ();
+ if (value > 32767 || value < -32767) {
+ rc.Report.Warning (8094, 1, Alignment.Location,
+ "Alignment value has a magnitude greater than 32767 and may result in a large formatted string");
+ }
+
+ return value;
+ }
}
}
3021, 3022, 3023, 3024, 3026, 3027,
4014, 4024, 4025, 4026,
7035, 7080, 7081, 7082, 7095,
- 8009,
+ 8009, 8094
};
static HashSet<int> AllWarningsHashSet;
public readonly PredefinedType INotifyCompletion;
public readonly PredefinedType ICriticalNotifyCompletion;
+ // C# 6.0
+ public readonly PredefinedType IFormattable;
+ public readonly PredefinedType FormattableString;
+ public readonly PredefinedType FormattableStringFactory;
+
public PredefinedTypes (ModuleContainer module)
{
TypedReference = new PredefinedType (module, MemberKind.Struct, "System", "TypedReference");
INotifyCompletion = new PredefinedType (module, MemberKind.Interface, "System.Runtime.CompilerServices", "INotifyCompletion");
ICriticalNotifyCompletion = new PredefinedType (module, MemberKind.Interface, "System.Runtime.CompilerServices", "ICriticalNotifyCompletion");
+ IFormattable = new PredefinedType (module, MemberKind.Interface, "System", "IFormattable");
+ FormattableString = new PredefinedType (module, MemberKind.Class, "System", "FormattableString");
+ FormattableStringFactory = new PredefinedType (module, MemberKind.Class, "System.Runtime.CompilerServices", "FormattableStringFactory");
+
//
// Define types which are used for comparison. It does not matter
// if they don't exist as no error report is needed
TaskGeneric.TypeSpec.IsGenericTask = true;
SwitchUserTypes = Switch.CreateSwitchUserTypes (module, Nullable.TypeSpec);
+
+ IFormattable.Define ();
+ FormattableString.Define ();
}
}
e9.Compile ().Invoke (1);
}
+ void CallTest_10 ()
+ {
+ Expression<Func<string>> e = () => $"{int.MaxValue}";
+ AssertNodeType (e, ExpressionType.Call);
+ Assert (int.MaxValue.ToString (), e.Compile ().Invoke ());
+ }
+
void CoalesceTest ()
{
Expression<Func<uint?, uint>> e = (uint? a) => a ?? 99;
--- /dev/null
+using System;
+using System.Linq.Expressions;
+
+namespace System
+{
+ public class FormattableString
+ {
+ public FormattableString (string str, object[] arguments)
+ {
+ Value = str;
+ Arguments = arguments;
+ }
+
+ public string Value { get; set; }
+ public object[] Arguments;
+ }
+}
+
+namespace System.Runtime.CompilerServices
+{
+ public static class FormattableStringFactory
+ {
+ public static object Create(string format, params object[] arguments)
+ {
+ if (format.StartsWith ("format"))
+ return new MyFormattable ();
+
+ return new FormattableString (format, arguments);
+ }
+ }
+}
+
+class MyFormattable : IFormattable
+{
+ string IFormattable.ToString (string str, IFormatProvider provider)
+ {
+ return null;
+ }
+}
+
+class ConversionTest
+{
+ static int Main ()
+ {
+ byte b = 3;
+
+ FormattableString c1;
+ c1 = $"{b}";
+ if (c1.Value != "{0}")
+ return 1;
+
+ IFormattable c2;
+ c2 = $"format { b }";
+ if (!(c2 is MyFormattable))
+ return 2;
+
+ return 0;
+ }
+}
\ No newline at end of file
<method name="Void NewTest_8()" attrs="129">\r
<size>140</size>\r
</method>\r
+ <method name="Void CallTest_10()" attrs="129">\r
+ <size>137</size>\r
+ </method>\r
</type>\r
</test>\r
<test name="gtest-etree-02.cs">\r
<test name="test-interpolation-01.cs">\r
<type name="Test">\r
<method name="Int32 Main()" attrs="150">\r
- <size>573</size>\r
+ <size>540</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
+ <test name="test-interpolation-02.cs">\r
+ <type name="System.FormattableString">\r
+ <method name="System.String get_Value()" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_Value(System.String)" attrs="2182">\r
+ <size>8</size>\r
+ </method>\r
+ <method name="Void .ctor(String, Object[])" attrs="6278">\r
+ <size>22</size>\r
+ </method>\r
+ </type>\r
+ <type name="System.Runtime.CompilerServices.FormattableStringFactory">\r
+ <method name="System.Object Create(System.String, System.Object[])" attrs="150">\r
+ <size>43</size>\r
+ </method>\r
+ </type>\r
+ <type name="MyFormattable">\r
+ <method name="System.String System.IFormattable.ToString(System.String, System.IFormatProvider)" attrs="481">\r
+ <size>10</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ <type name="ConversionTest">\r
+ <method name="Int32 Main()" attrs="145">\r
+ <size>120</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r