2 // codegen.cs: The code generator
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 Ximian, Inc.
12 using System.Collections;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Runtime.InteropServices;
16 using System.Security.Cryptography;
18 using Mono.Security.Cryptography;
20 namespace Mono.CSharp {
23 /// Code generator class.
25 public class CodeGen {
26 static AppDomain current_domain;
27 static public SymbolWriter SymbolWriter;
29 public static AssemblyClass Assembly;
30 public static ModuleClass Module;
34 Assembly = new AssemblyClass ();
35 Module = new ModuleClass (RootContext.Unsafe);
38 public static string Basename (string name)
40 int pos = name.LastIndexOf ('/');
43 return name.Substring (pos + 1);
45 pos = name.LastIndexOf ('\\');
47 return name.Substring (pos + 1);
52 public static string Dirname (string name)
54 int pos = name.LastIndexOf ('/');
57 return name.Substring (0, pos);
59 pos = name.LastIndexOf ('\\');
61 return name.Substring (0, pos);
66 static string TrimExt (string name)
68 int pos = name.LastIndexOf ('.');
70 return name.Substring (0, pos);
73 static public string FileName;
76 // Initializes the symbol writer
78 static void InitializeSymbolWriter ()
80 SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder);
83 // If we got an ISymbolWriter instance, initialize it.
85 if (SymbolWriter == null) {
87 -18, "Could not find the symbol writer assembly (Mono.CSharp.Debugger.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CSharp.Debugger directory.");
93 // Initializes the code generator variables
95 static public void Init (string name, string output, bool want_debugging_support)
98 AssemblyName an = Assembly.GetAssemblyName (name, output);
100 current_domain = AppDomain.CurrentDomain;
103 Assembly.Builder = current_domain.DefineDynamicAssembly (an,
104 AssemblyBuilderAccess.Save, Dirname (name));
106 catch (ArgumentException) {
107 // specified key may not be exportable outside it's container
108 if (RootContext.StrongNameKeyContainer != null) {
109 Report.Error (1548, "Could not access the key inside the container `" +
110 RootContext.StrongNameKeyContainer + "'.");
111 Environment.Exit (1);
115 catch (CryptographicException) {
116 if ((RootContext.StrongNameKeyContainer != null) || (RootContext.StrongNameKeyFile != null)) {
117 Report.Error (1548, "Could not use the specified key to strongname the assembly.");
118 Environment.Exit (1);
124 // Pass a path-less name to DefineDynamicModule. Wonder how
125 // this copes with output in different directories then.
126 // FIXME: figure out how this copes with --output /tmp/blah
128 // If the third argument is true, the ModuleBuilder will dynamically
129 // load the default symbol writer.
131 Module.Builder = Assembly.Builder.DefineDynamicModule (
132 Basename (name), Basename (output), want_debugging_support);
134 if (want_debugging_support)
135 InitializeSymbolWriter ();
138 static public void Save (string name)
141 Assembly.Builder.Save (Basename (name));
143 catch (COMException) {
144 if ((RootContext.StrongNameKeyFile == null) || (!RootContext.StrongNameDelaySign))
147 // FIXME: it seems Microsoft AssemblyBuilder doesn't like to delay sign assemblies
148 Report.Error (1548, "Couldn't delay-sign the assembly with the '" +
149 RootContext.StrongNameKeyFile +
150 "', Use MCS with the Mono runtime or CSC to compile this assembly.");
152 catch (System.IO.IOException io) {
153 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
159 // Provides "local" store across code that can yield: locals
160 // or fields, notice that this should not be used by anonymous
161 // methods to create local storage, those only require
164 public class VariableStorage {
171 public VariableStorage (EmitContext ec, Type t)
175 fb = IteratorHandler.Current.MapVariable ("s_", count.ToString (), t);
177 local = ec.ig.DeclareLocal (t);
181 public void EmitThis ()
184 ig.Emit (OpCodes.Ldarg_0);
187 public void EmitStore ()
190 ig.Emit (OpCodes.Stloc, local);
192 ig.Emit (OpCodes.Stfld, fb);
195 public void EmitLoad ()
198 ig.Emit (OpCodes.Ldloc, local);
200 ig.Emit (OpCodes.Ldfld, fb);
203 public void EmitCall (MethodInfo mi)
205 // FIXME : we should handle a call like tostring
206 // here, where boxing is needed. However, we will
207 // never encounter that with the current usage.
209 bool value_type_call;
212 value_type_call = local.LocalType.IsValueType;
215 ig.Emit (OpCodes.Ldloca, local);
217 ig.Emit (OpCodes.Ldloc, local);
219 value_type_call = fb.FieldType.IsValueType;
222 ig.Emit (OpCodes.Ldflda, fb);
224 ig.Emit (OpCodes.Ldfld, fb);
227 ig.Emit (value_type_call ? OpCodes.Call : OpCodes.Callvirt, mi);
232 /// An Emit Context is created for each body of code (from methods,
233 /// properties bodies, indexer bodies or constructor bodies)
235 public class EmitContext {
236 public DeclSpace DeclSpace;
237 public DeclSpace TypeContainer;
238 public ILGenerator ig;
241 /// This variable tracks the `checked' state of the compilation,
242 /// it controls whether we should generate code that does overflow
243 /// checking, or if we generate code that ignores overflows.
245 /// The default setting comes from the command line option to generate
246 /// checked or unchecked code plus any source code changes using the
247 /// checked/unchecked statements or expressions. Contrast this with
248 /// the ConstantCheckState flag.
251 public bool CheckState;
254 /// The constant check state is always set to `true' and cant be changed
255 /// from the command line. The source code can change this setting with
256 /// the `checked' and `unchecked' statements and expressions.
258 public bool ConstantCheckState;
261 /// Whether we are emitting code inside a static or instance method
263 public bool IsStatic;
266 /// Whether we are emitting a field initializer
268 public bool IsFieldInitializer;
271 /// The value that is allowed to be returned or NULL if there is no
274 public Type ReturnType;
277 /// Points to the Type (extracted from the TypeContainer) that
278 /// declares this body of code
280 public Type ContainerType;
283 /// Whether this is generating code for a constructor
285 public bool IsConstructor;
288 /// Whether we're control flow analysis enabled
290 public bool DoFlowAnalysis;
293 /// Keeps track of the Type to LocalBuilder temporary storage created
294 /// to store structures (used to compute the address of the structure
295 /// value on structure method invocations)
297 public Hashtable temporary_storage;
299 public Block CurrentBlock;
301 public int CurrentFile;
304 /// The location where we store the return value.
306 LocalBuilder return_value;
309 /// The location where return has to jump to return the
312 public Label ReturnLabel;
315 /// If we already defined the ReturnLabel
317 public bool HasReturnLabel;
320 /// Whether we are inside an iterator block.
322 public bool InIterator;
324 public bool IsLastStatement;
327 /// Whether remapping of locals, parameters and fields is turned on.
328 /// Used by iterators and anonymous methods.
330 public bool RemapToProxy;
333 /// Whether we are inside an unsafe block
335 public bool InUnsafe;
338 /// Whether we are in a `fixed' initialization
340 public bool InFixedInitializer;
343 /// Whether we are inside an anonymous method.
345 public bool InAnonymousMethod;
348 /// Location for this EmitContext
353 /// Used to flag that it is ok to define types recursively, as the
354 /// expressions are being evaluated as part of the type lookup
355 /// during the type resolution process
357 public bool ResolvingTypeTree;
360 /// Inside an enum definition, we do not resolve enumeration values
361 /// to their enumerations, but rather to the underlying type/value
362 /// This is so EnumVal + EnumValB can be evaluated.
364 /// There is no "E operator + (E x, E y)", so during an enum evaluation
365 /// we relax the rules
367 public bool InEnumContext;
369 FlowBranching current_flow_branching;
371 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
372 Type return_type, int code_flags, bool is_constructor)
376 TypeContainer = parent;
378 CheckState = RootContext.Checked;
379 ConstantCheckState = true;
381 IsStatic = (code_flags & Modifiers.STATIC) != 0;
382 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
383 RemapToProxy = InIterator;
384 ReturnType = return_type;
385 IsConstructor = is_constructor;
390 // Can only be null for the ResolveType contexts.
391 ContainerType = parent.TypeBuilder;
392 if (parent.UnsafeContext)
395 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
399 if (ReturnType == TypeManager.void_type)
403 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
404 Type return_type, int code_flags, bool is_constructor)
405 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
409 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
410 Type return_type, int code_flags)
411 : this (tc, tc, l, ig, return_type, code_flags, false)
415 public FlowBranching CurrentBranching {
417 return current_flow_branching;
422 // Starts a new code branching. This inherits the state of all local
423 // variables and parameters from the current branching.
425 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
427 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
428 return current_flow_branching;
432 // Starts a new code branching for block `block'.
434 public FlowBranching StartFlowBranching (Block block)
436 FlowBranching.BranchingType type;
438 if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
439 type = FlowBranching.BranchingType.SwitchSection;
441 type = FlowBranching.BranchingType.Block;
443 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
444 return current_flow_branching;
448 // Ends a code branching. Merges the state of locals and parameters
449 // from all the children of the ending branching.
451 public FlowBranching.UsageVector DoEndFlowBranching ()
453 FlowBranching old = current_flow_branching;
454 current_flow_branching = current_flow_branching.Parent;
456 return current_flow_branching.MergeChild (old);
460 // Ends a code branching. Merges the state of locals and parameters
461 // from all the children of the ending branching.
463 public FlowBranching.Reachability EndFlowBranching ()
465 FlowBranching.UsageVector vector = DoEndFlowBranching ();
467 return vector.Reachability;
471 // Kills the current code branching. This throws away any changed state
472 // information and should only be used in case of an error.
474 public void KillFlowBranching ()
476 current_flow_branching = current_flow_branching.Parent;
479 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
481 bool unreachable = false;
483 if (!Location.IsNull (loc))
484 CurrentFile = loc.File;
488 int errors = Report.Errors;
490 block.EmitMeta (this, ip);
492 if (Report.Errors == errors){
493 bool old_do_flow_analysis = DoFlowAnalysis;
494 DoFlowAnalysis = true;
496 current_flow_branching = FlowBranching.CreateBranching (
497 null, FlowBranching.BranchingType.Block, block, loc);
499 if (!block.Resolve (this)) {
500 current_flow_branching = null;
501 DoFlowAnalysis = old_do_flow_analysis;
505 FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
506 current_flow_branching = null;
508 DoFlowAnalysis = old_do_flow_analysis;
512 if (reachability.AlwaysReturns ||
513 reachability.AlwaysThrows ||
514 reachability.IsUnreachable)
518 } catch (Exception e) {
519 Console.WriteLine ("Exception caught by the compiler while compiling:");
520 Console.WriteLine (" Block that caused the problem begin at: " + loc);
522 if (CurrentBlock != null){
523 Console.WriteLine (" Block being compiled: [{0},{1}]",
524 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
526 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
527 Console.WriteLine (Report.FriendlyStackTrace (e));
529 Environment.Exit (1);
536 if (ReturnType != null && !unreachable){
538 Report.Error (161, loc, "Not all code paths return a value");
544 ig.MarkLabel (ReturnLabel);
545 if (return_value != null){
546 ig.Emit (OpCodes.Ldloc, return_value);
547 ig.Emit (OpCodes.Ret);
550 // If `HasReturnLabel' is set, then we already emitted a
551 // jump to the end of the method, so we must emit a `ret'
554 // Unfortunately, System.Reflection.Emit automatically emits
555 // a leave to the end of a finally block. This is a problem
556 // if no code is following the try/finally block since we may
557 // jump to a point after the end of the method.
558 // As a workaround, we're always creating a return label in
562 if ((block != null) && block.IsDestructor) {
563 // Nothing to do; S.R.E automatically emits a leave.
564 } else if (HasReturnLabel || (!unreachable && !InIterator)) {
565 if (ReturnType != null)
566 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
567 ig.Emit (OpCodes.Ret);
573 /// This is called immediately before emitting an IL opcode to tell the symbol
574 /// writer to which source line this opcode belongs.
576 public void Mark (Location loc, bool check_file)
578 if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
581 if (check_file && (CurrentFile != loc.File))
584 ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
588 /// Returns a temporary storage for a variable of type t as
589 /// a local variable in the current body.
591 public LocalBuilder GetTemporaryLocal (Type t)
593 LocalBuilder location = null;
595 if (temporary_storage != null){
596 object o = temporary_storage [t];
599 ArrayList al = (ArrayList) o;
601 for (int i = 0; i < al.Count; i++){
603 location = (LocalBuilder) al [i];
609 location = (LocalBuilder) o;
610 if (location != null)
615 return ig.DeclareLocal (t);
618 public void FreeTemporaryLocal (LocalBuilder b, Type t)
620 if (temporary_storage == null){
621 temporary_storage = new Hashtable ();
622 temporary_storage [t] = b;
625 object o = temporary_storage [t];
627 temporary_storage [t] = b;
631 ArrayList al = (ArrayList) o;
632 for (int i = 0; i < al.Count; i++){
641 ArrayList replacement = new ArrayList ();
643 temporary_storage.Remove (t);
644 temporary_storage [t] = replacement;
648 /// Current loop begin and end labels.
650 public Label LoopBegin, LoopEnd;
653 /// Default target in a switch statement. Only valid if
656 public Label DefaultTarget;
659 /// If this is non-null, points to the current switch statement
661 public Switch Switch;
664 /// ReturnValue creates on demand the LocalBuilder for the
665 /// return value from the function. By default this is not
666 /// used. This is only required when returns are found inside
667 /// Try or Catch statements.
669 public LocalBuilder TemporaryReturn ()
671 if (return_value == null){
672 return_value = ig.DeclareLocal (ReturnType);
673 ReturnLabel = ig.DefineLabel ();
674 HasReturnLabel = true;
680 public void NeedReturnLabel ()
682 if (!HasReturnLabel) {
683 ReturnLabel = ig.DefineLabel ();
684 HasReturnLabel = true;
689 // Creates a field `name' with the type `t' on the proxy class
691 public FieldBuilder MapVariable (string name, Type t)
694 return IteratorHandler.Current.MapVariable ("v_", name, t);
697 throw new Exception ("MapVariable for an unknown state");
701 // Invoke this routine to remap a VariableInfo into the
702 // proper MemberAccess expression
704 public Expression RemapLocal (LocalInfo local_info)
706 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
707 fe.InstanceExpression = new ProxyInstance ();
708 return fe.DoResolve (this);
711 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
713 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
714 fe.InstanceExpression = new ProxyInstance ();
715 return fe.DoResolveLValue (this, right_side);
718 public Expression RemapParameter (int idx)
720 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
721 fe.InstanceExpression = new ProxyInstance ();
722 return fe.DoResolve (this);
725 public Expression RemapParameterLValue (int idx, Expression right_side)
727 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
728 fe.InstanceExpression = new ProxyInstance ();
729 return fe.DoResolveLValue (this, right_side);
733 // Emits the proper object to address fields on a remapped
734 // variable/parameter to field in anonymous-method/iterator proxy classes.
736 public void EmitThis ()
739 ig.Emit (OpCodes.Ldarg_0);
741 FieldBuilder this_field = IteratorHandler.Current.this_field;
742 if (TypeManager.IsValueType (this_field.FieldType))
743 ig.Emit (OpCodes.Ldflda, this_field);
745 ig.Emit (OpCodes.Ldfld, this_field);
748 ig.Emit (OpCodes.Ldarg_0);
751 public Expression GetThis (Location loc)
754 if (CurrentBlock != null)
755 my_this = new This (CurrentBlock, loc);
757 my_this = new This (loc);
759 if (!my_this.ResolveBase (this))
767 public abstract class CommonAssemblyModulClass: Attributable {
768 static string[] attribute_targets = new string [] { "assembly", "module" };
770 protected CommonAssemblyModulClass ():
775 public void AddAttributes (ArrayList attrs)
777 if (OptAttributes == null) {
778 OptAttributes = new Attributes (attrs);
781 OptAttributes.AddAttributes (attrs);
782 OptAttributes.CheckTargets (ValidAttributeTargets);
785 public virtual void Emit (TypeContainer tc)
787 if (OptAttributes == null)
790 EmitContext ec = new EmitContext (tc, Mono.CSharp.Location.Null, null, null, 0, false);
791 OptAttributes.Emit (ec, this);
794 protected Attribute GetClsCompliantAttribute ()
796 if (OptAttributes == null)
799 EmitContext temp_ec = new EmitContext (new TypeContainer (), Mono.CSharp.Location.Null, null, null, 0, false);
800 Attribute a = OptAttributes.Search (TypeManager.cls_compliant_attribute_type, temp_ec);
807 protected override string[] ValidAttributeTargets {
809 return attribute_targets;
814 public class AssemblyClass: CommonAssemblyModulClass {
815 // TODO: make it private and move all builder based methods here
816 public AssemblyBuilder Builder;
818 bool is_cls_compliant;
820 public AssemblyClass (): base ()
822 is_cls_compliant = false;
825 public bool IsClsCompliant {
827 return is_cls_compliant;
831 public override AttributeTargets AttributeTargets {
833 return AttributeTargets.Assembly;
837 public override bool IsClsCompliaceRequired(DeclSpace ds)
839 return is_cls_compliant;
842 public void ResolveClsCompliance ()
844 Attribute a = GetClsCompliantAttribute ();
848 is_cls_compliant = a.GetClsCompliantAttributeValue (null);
851 public AssemblyName GetAssemblyName (string name, string output)
853 if (OptAttributes != null) {
854 foreach (Attribute a in OptAttributes.Attrs) {
855 if (a.Target != "assembly")
857 // TODO: This code is buggy: comparing Attribute name without resolving it is wrong.
858 // However, this is invoked by CodeGen.Init, at which time none of the namespaces
861 case "AssemblyKeyFile":
862 case "AssemblyKeyFileAttribute":
863 case "System.Reflection.AssemblyKeyFileAttribute":
864 if (RootContext.StrongNameKeyFile != null) {
865 Report.Warning (1616, "Compiler option -keyfile overrides " +
866 "AssemblyKeyFileAttribute");
869 string value = a.GetString ();
870 if (value != String.Empty)
871 RootContext.StrongNameKeyFile = value;
874 case "AssemblyKeyName":
875 case "AssemblyKeyNameAttribute":
876 case "System.Reflection.AssemblyKeyNameAttribute":
877 if (RootContext.StrongNameKeyContainer != null) {
878 Report.Warning (1616, "Compiler option -keycontainer overrides " +
879 "AssemblyKeyNameAttribute");
882 string value = a.GetString ();
883 if (value != String.Empty)
884 RootContext.StrongNameKeyContainer = value;
887 case "AssemblyDelaySign":
888 case "AssemblyDelaySignAttribute":
889 case "System.Reflection.AssemblyDelaySignAttribute":
890 RootContext.StrongNameDelaySign = a.GetBoolean ();
896 AssemblyName an = new AssemblyName ();
897 an.Name = Path.GetFileNameWithoutExtension (name);
899 // note: delay doesn't apply when using a key container
900 if (RootContext.StrongNameKeyContainer != null) {
901 an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer);
905 // strongname is optional
906 if (RootContext.StrongNameKeyFile == null)
909 string AssemblyDir = Path.GetDirectoryName (output);
911 // the StrongName key file may be relative to (a) the compiled
912 // file or (b) to the output assembly. See bugzilla #55320
913 // http://bugzilla.ximian.com/show_bug.cgi?id=55320
915 // (a) relative to the compiled file
916 string filename = Path.GetFullPath (RootContext.StrongNameKeyFile);
917 bool exist = File.Exists (filename);
918 if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) {
919 // (b) relative to the outputed assembly
920 filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile));
921 exist = File.Exists (filename);
925 using (FileStream fs = new FileStream (filename, FileMode.Open, FileAccess.Read)) {
926 byte[] snkeypair = new byte [fs.Length];
927 fs.Read (snkeypair, 0, snkeypair.Length);
929 if (RootContext.StrongNameDelaySign) {
930 // delayed signing - DO NOT include private key
932 // check for possible ECMA key
933 if (snkeypair.Length == 16) {
934 // will be rejected if not "the" ECMA key
935 an.KeyPair = new StrongNameKeyPair (snkeypair);
938 // take it, with or without, a private key
939 RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair);
940 // and make sure we only feed the public part to Sys.Ref
941 byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
942 an.KeyPair = new StrongNameKeyPair (publickey);
946 Report.Error (1548, "Could not strongname the assembly. File `" +
947 RootContext.StrongNameKeyFile + "' incorrectly encoded.");
948 Environment.Exit (1);
952 // no delay so we make sure we have the private key
954 CryptoConvert.FromCapiPrivateKeyBlob (snkeypair);
955 an.KeyPair = new StrongNameKeyPair (snkeypair);
957 catch (CryptographicException) {
958 if (snkeypair.Length == 16) {
959 // error # is different for ECMA key
960 Report.Error (1606, "Could not strongname the assembly. " +
961 "ECMA key can only be used to delay-sign assemblies");
964 Report.Error (1548, "Could not strongname the assembly. File `" +
965 RootContext.StrongNameKeyFile +
966 "' doesn't have a private key.");
968 Environment.Exit (1);
974 Report.Error (1548, "Could not strongname the assembly. File `" +
975 RootContext.StrongNameKeyFile + "' not found.");
976 Environment.Exit (1);
981 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
983 Builder.SetCustomAttribute (customBuilder);
987 public class ModuleClass: CommonAssemblyModulClass {
988 // TODO: make it private and move all builder based methods here
989 public ModuleBuilder Builder;
990 bool m_module_is_unsafe;
992 public ModuleClass (bool is_unsafe)
994 m_module_is_unsafe = is_unsafe;
997 public override AttributeTargets AttributeTargets {
999 return AttributeTargets.Module;
1003 public override bool IsClsCompliaceRequired(DeclSpace ds)
1005 return CodeGen.Assembly.IsClsCompliant;
1008 public override void Emit (TypeContainer tc)
1012 if (!m_module_is_unsafe)
1015 if (TypeManager.unverifiable_code_ctor == null) {
1016 Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
1020 ApplyAttributeBuilder (null, new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
1023 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
1025 if (a != null && a.Type == TypeManager.cls_compliant_attribute_type) {
1026 Report.Warning_T (3012, a.Location);
1030 Builder.SetCustomAttribute (customBuilder);